Problem with simple Inheritance for class representing arithmetic expressions - python

I have a problem implementing a simple class hierarchy with arithmetic expressions.
TypeError: unsupported operand type(s) for *: 'NoneType' and 'int'
and the same for the other operations, addition, subtraction, division, etc.
My code:
from abc import ABC, abstractmethod
class Wyrazenie(ABC):
# #abstractmethod
def Oblicz(self):
pass
class Operator(Wyrazenie):
w1 = Wyrazenie()
w2 = Wyrazenie()
def __init__(self, wy1, wy2):
self.w1 = wy1
self.w2 = wy2
#def Oblicz(self) -> int:
# pass
class Zmienna(Wyrazenie):
zmienna=""
slownik={}
def __init__(self, klucz, wartosc):
self.slownik.update({klucz: wartosc})
def Oblicz(self):
return self.slownik.get(self.zmienna)
class Stala(Wyrazenie):
wartosc=0
def __init__(self, wartosc1):
self.wartosc=wartosc1
def Oblicz(self):
return self.wartosc
class Dodaj(Operator):
def __init__(self, wy1=None, wy2=None):
super().__init__(wy1, wy2)
def Oblicz(self) -> int:
return self.w1.Oblicz() + self.w2.Oblicz()
class Odejmij(Operator):
def __init__(self, wy1=None, wy2=None):
super().__init__(wy1,wy2)
def Oblicz(self) -> int:
return self.w1.Oblicz() - self.w2.Oblicz()
class Pomnoz(Operator):
def __init__(self, wy1=None, wy2=None):
super().__init__(wy1,wy2)
def Oblicz(self) -> int:
return self.w1.Oblicz() * self.w2.Oblicz()
class Podziel(Operator):
def __init__(self, wy1=None, wy2=None):
super().__init__(wy1,wy2)
def Oblicz(self) -> int:
return self.w1.Oblicz() / self.w2.Oblicz()
z1=Zmienna("x", 4)
z2=Zmienna("y", 10)
# 4 * 10 + 10 - 5 = 45
wyr = Dodaj(Pomnoz(z1, Stala(10)), Odejmij(z2, Stala(5)))
wyr.Oblicz()
print("Wartosc wyrazenia= \n")
print(wyr.Oblicz())
I don't know whether it's an issue with initializing wy1, wy2 with None or whole wrong idea of representing such expressions. I tried to rewrite it from my Java class.

I'm not sure why you're adding so much complication before fixing something basic, but here's what's happening:
you call wyr.Oblicz(), which is Dodaj.Oblicz() (by the way, bad idea to name a method with capitals, it's very confusing as it looks like a class)
it calls self.w1.Oblicz() before trying sum the result with something else, and it is the Oblicz() of the first parameter passed to Dodaj(Pomnoz(z1, Stala(10)), Odejmij(z2, Stala(5))), so Pomnoz.Oblicz()
it calls self.w1.Oblicz() before trying to multiply the result with something else, and it is the first parameter of Pomnoz, i.e. zi, which is Zmienna("x", 4), so Zmienna.Oblicz()
it calls self.slownik.get(self.zmienna), but since self.zmienna is "" and there is no entry for "" in self.slownik, it returns None
That None is passed back and the multiplication then fails when Pomnoz.Oblicz() tries to perform it, as it cannot multiply None and an integer.
If you were using an IDE with a debugger, you could trace that yourself, by stepping into code as it's calling further functions - consider using something like PyCharm, Spyder, VSCode or any other or editor or IDE that has a debugging facility.

I've traced your code down, and I found that the reason this error is occurring is ultimately because Zmienna.Oblicz is returning None.
The call wyr.Oblicz() is like this:
wyr (Dodaj) .Oblicz ->
wyr.w1 (Pomnoz) .Oblicz ->
wyr.w1.w1 (z1) (Zmienna) .Oblicz ->
self (Zmienna) .slownik.get(self.zmienna) ->
this call returned None
At the time slownik.get was called, slownik was a dict containing {'x': 4, 'y': 10}, and self.zmienna was an str containing "".

Related

Python typing: mypy complains about bound TypeVar having incompatible type with subclass of that type

The following code results in the error 28: error: List item 0 has incompatible type "ExampleRule[<nothing>]"; expected "RuleSub" [list-item]. How can I change the type annotations to be correct?
from typing import *
EdgeSub = TypeVar('EdgeSub', bound='EdgeBase')
RuleSub = TypeVar('RuleSub', bound='RuleBase')
class EdgeBase:
pass
class RuleBase(Generic[EdgeSub]):
def proposeEdges(self) -> list[EdgeSub]:
return []
def allowEdge(self, edge: EdgeSub) -> bool:
return True
class ExampleRule(RuleBase[EdgeSub]):
def proposeEdges(self) -> list[EdgeSub]:
return []
def allowEdge(self, edge: EdgeSub) -> bool:
return True
def get_rule_list() -> list[RuleSub]:
weapon_rule_list: list[RuleSub] = [ExampleRule()] # This is line 28 with the error
return weapon_rule_list
Note that in the non-simplified implementation, ExampleRule.proposeEdges returns a list of instances of many different subclasses of EdgeBase, hence needing the EdgeSub type var. Similarly, get_rule_list needs to return many different types of rules.

How to best implement a class method that is different for every object of this class

As a teacher I want to code a worksheet-generator for mathematical problems.
Python should generate mathematical problems and their solution (e.g. create a polynomial function and calculate their zeros). It then writes a LaTeX input file and creates a pdf via pdflatex. It works for several problems, but now I want to generalize it and make it object-orientated to speed up the creation of further worksheets.
So I created a class Problem with several parameters and methods. But every Problem-instance should have a different function for creating the text for the mathematical problem and its solution (because every object is a different mathematical problem). And I've got no clue how I should manage this in an elegant/proper way.
At the moment I'm doing something like this:
import random
class Problem_generator(object):
def __init__(self):
self.problemlist = []
def create_worksheet(self):
""" Creates the latex-document by writing the output in a LaTeX input file. For
this minimal example, I'll just print the outputs instead.
"""
for problem in self.problemlist:
text = problem.create()
print("Problem: " + text[0])
print("Solution: " + text[1])
class Problem(object):
def __init__(self, problem_generator, function):
# do some stuff like to create Tkinter-GUI-objects for every problem
self.function = function
problem_generator.problemlist.append(self)
def create(self):
return self.function()
def add_numbers():
""" Create the problem to add two numbers. """
a, b = random.randint(0, 100), random.randint(0, 100)
problem_text = str(a) + " + " + str(b)
solution_text = str(a+b)
return problem_text, solution_text
generator = Problem_generator()
problem_1 = Problem(generator, add_numbers)
generator.create_worksheet() # in the complete program I start this over a GUI
It works alright, but it doesn't feel "right".
I also thought about implementing the Problem class with a create() method that only raises a not-implemented-error and then to update the create() method for every problem I create. But as far as I read, that would update the create() method for every object of the class.
So I would be happy to get some tips/suggestions how I could manage the described problem in an "elegant" way.
Here's the way I'd do it:
import random
from typing import Callable, List, Tuple
ProblemCreator = Callable[[], Tuple[str, str]] # returns (problem, solution)
class Problem:
def __init__(self, function: ProblemCreator) -> None:
# do some stuff like to create Tkinter-GUI-objects for every problem
self.create = function
class ProblemGenerator:
def __init__(self) -> None:
self.problem_list: List[Problem] = []
def create_worksheet(self) -> None:
"""
Creates the latex-document by writing the output in a *.tex-file.
For this minimal example, I'll just print the outputs instead.
"""
for problem in self.problem_list:
p, s = problem.create()
print(f"Problem: {p}")
print(f"Solution: {s}")
def generate_problem(self, problem: ProblemCreator) -> None:
self.problem_list.append(Problem(problem))
def add_numbers() -> Tuple[str, str]:
""" Create the problem to add two numbers. """
a, b = random.randint(0, 100), random.randint(0, 100)
return f"{a} + {b}", f"{a+b}"
generator = ProblemGenerator()
generator.generate_problem(add_numbers)
generator.create_worksheet() # in the complete program I start this over a GUI
I've used type annotations and other Python 3 features (like f-strings) to improve clarity.
There is no need for create to be a method -- just make it a callable attribute (I've given the type of this callable a name, ProblemCreator, since it forms an important part of this interface). Similarly, there's no need for Problem to know about ProblemGenerator and be responsible for adding itself to the generator's list; it just creates a circular dependency that you don't need. Instead, have ProblemGenerator be in charge of generating problems (like its name says)!
I really don't find any use for the Problem_generator class, here is how I would do it (I added a ProblemGenerator class but you can loop and call problem.create)
By using this you can
define problems as a subclass of Problem (see AddTwoNumbers class)
define problems as functions (see subtract_numbers function) and use the problem_decorator to convert them to FunctionProblems (subclass of Problem).
from abc import ABC, abstractmethod
from functools import wraps
import random
from typing import Callable, List, Tuple
random.seed(0) # for reproducability
class Problem(ABC):
#property
#abstractmethod
def text(self) -> str:
pass
#property
#abstractmethod
def solution_text(self) -> str:
pass
class AddTwoNumbers(Problem):
def __init__(self) -> None:
self._a = random.randint(0, 100)
self._b = random.randint(0, 100)
#property
def text(self) -> str:
return f'{self._a} + {self._b}'
#property
def solution_text(self) -> str:
return str(self._a + self._b)
# If you want to define functions as problems you can do something like that
class FunctionProblem(Problem):
def __init__(self, func: Callable[[], Tuple[str, str]]):
self._text, self._solution_text = func()
#property
def text(self) -> str:
return self._text
#property
def solution_text(self) -> str:
return self._solution_text
# Define a decorator so that functions become FunctionProblems
def problem_decorator(func: Callable[[], Tuple[str, str]]) -> Callable[[], FunctionProblem]:
#wraps(func)
def wrapper():
return FunctionProblem(func)
return wrapper
#problem_decorator
def subtract_numbers() -> Tuple[str, str]:
a, b = random.randint(0, 100), random.randint(0, 100)
text = f'{a} - {b}'
solution = str(a - b)
return text, solution
# If you really want to define a ProblemGenerator
class ProblemGenerator:
def __init__(self, *problems: Problem) -> None:
self.problems = list(problems)
def add_problem(self, problem: Problem) -> None:
self.problems.append(problem)
def create_worksheet(self) -> List[Tuple[str, str]]:
for problem in self.problems:
print(f'Problem text is {problem.text!r}, Solution is {problem.solution_text!r}')
generator = ProblemGenerator(AddTwoNumbers())
generator.add_problem(subtract_numbers())
generator.create_worksheet()
prints
Problem text is '49 + 97', Solution is '146'
Problem text is '53 - 5', Solution is '48'
As I already said in a comment, the classic object-oriented programming (OOP) way to do handle such a scenario is to define an abstract base class with the overall interface and perhaps some generic helper methods and then define problem-specific "concrete" subclasses. Here's a toy example of doing something like that on Python it with based on the code in your question. If it seems like you're creating a bunch of almost-the-same classes. Sometimes the remedy for that is to subclass your subclasses, but often it just means you haven't abstracted the problem well…
From the comments in your sample code, it sounds like you also want have your classes be responsible for creating their own tkinter objects for a graphical user interface (GUI). Generally speaking that's probably not a good idea — classes should generally only have a single responsibility, which mean trying pile many of them on can greatly complicate matters by make testing, debugging, and extending what you have a lot more difficult.
There's a commonly used software design pattern called model–view–controller (MVC) that is commonly used for developing user interfaces that a program up into three interconnected components, but keeps each one separate to reduce complexity — so I suggest you invest a the time studying it.
import abc
import random
class Problem(metaclass=abc.ABCMeta):
""" Abstract base class of all mathematical problems. """
def __init__(self):
self.lines = []
#abc.abstractmethod
def create_problem(self):
""" Creates input to be fed to the create_worksheet method. """
...
def create_worksheet(self, indent=4, char=' '):
""" Creates the latex-document by writing the output in a LaTeX input file.
In this toy example, it just print the lines the create_problem() method
generated.
"""
padding = indent * char
for line in self.lines:
print(padding + line)
class AddNumbers(Problem):
def __init__(self):
super().__init__() # Initialize base class.
self.create_problem() # Create subclass-specific data.
def create_problem(self):
a, b = random.randint(0, 100), random.randint(0, 100)
self.lines.append(f'Problem: Add the two numbers {a} and {b} together')
self.lines.append(f'Solution: {a+b}')
if __name__ == '__main__':
# Create some sample Problem subclass instances.
problems = [AddNumbers(), AddNumbers()]
# Create worksheet the worksheet for each one.
for i, problem in enumerate(problems, start=1):
print(f'Worksheet {i}:')
problem.create_worksheet(indent=2)

How to print the for loop in a class object?

Hi I am learning about OOP and classes and have some confusion regarding the implementation of classes. Below, I have two sets of identical codes with slight differences in the way I am trying to run them after defining the methods in the class. I am just unsure as to why the first code runs and the second one doesnt as I feel like I am doing the same thing? Also, upon running the first code I get 'None' after the loop has finished, any ideas why that takes place? In addition to this, I know that there are iterators that you can use but just wondering if the method used below to loop through stuff is incorrect or problematic?
class i:
def __init__(self,number):
self.number=number
def fun(self):
for i in range(self.number):
print(i)
kj=i(9)
print(kj.fun())
class i:
def __init__(self,number):
self.number=number
def fun(self):
for i in range(self.number):
print(i)
kj=i()
print(kj.fun(9))
In python, class names are typically capitalized. This avoids the problem here where you define a class with the same name are your index variable.
Also, if you want to print a result of a method, then have the method return a value rather than having it print each value.
Putting these concepts together, I would change
class i:
def __init__(self, number):
self.number = number
def fun(self):
for i in range(self.number):
print(i)
kj=i(9)
print(kj.fun())
to
class I:
def __init__(self, number):
self.number = number
def fun(self, number=None):
if not number:
number = self.number
return list(range(number))
kj = I(9)
numbers = kj.fun()
# if you want to print the list
print(numbers)
# if you want to print individual values
print(*numbers)
# if you want to print one number per line
print('\n'.join(numbers))
The following code illustrates the syntax better with type hint:
class i:
def __init__(self,number: int) -> "i":
self.number=number
def fun(self) -> None:
for i in range(self.number):
print(f"fun - i")
kj=i(9)
print(kj.fun())
The output
-> fun - 0
-> fun - 1
-> fun - 2
-> fun - 3
-> fun - 4
-> fun - 5
-> fun - 6
-> fun - 7
-> fun - 8
-> None
We see that the output 1 - 8 is from within the function
And None is because the fun() method has None return by default.
The second code will have the following error
TypeError: __init__() missing 1 required positional argument: 'number'
because to initialise the class it expects the number input. The fact that you want to pass the parameter on the 'fun' method call, it means to expect the number on class initialisation is redundant;
One can do the following instead:
class i:
def __init__(self):
pass
def fun(self, number: int):
for i in range(number):
print(i)
kj = i()
print(kj.fun(9))

Python object as property type

I'm searching for an elegant way to replace setter/getter methodes handling complex data types by properties using the #property decorator.
The class I'm working on should represent some kind of (network) dimmer. It is possible to request/send "resources" addressed by a specific ID to control the device. I'd like to represent those "resources" as properties of my class hiding the request/send mechanism and the cryptical ID numbers.
Some of those "resources" are just primitive types (int, float, ...) but some are more complex, so I've just created simple classes for them.
This works fine, but there is an ugly source of error: It is not possible to change an attribute of that property directly, I have to set the property completely everytime.
DUMMY_DB = {0x0001: bytearray([0x00])}
class State:
def __init__(self, on, value):
self.on = on
self.value = value
#staticmethod
def from_int(val):
return State(bool(val & 0x80), val & 0x7f)
def __int__(self):
return self.on << 7 | self.value
class Dimmer:
#property
def state(self) -> State:
return State.from_int(self._request(0x0001)[0]) # ID 0x0001 => State
#state.setter
def state(self, val: State):
self._send(0x0001, [int(val)]) # ID 0x0001 => State
# several more properties...
def _request(self, ident) -> bytearray:
# usually get resource 'ident' from network/file/...
return DUMMY_DB[ident]
def _send(self, ident, value):
# usually set resource 'ident' on network/file/... using value
DUMMY_DB[ident] = value
if __name__ == '__main__':
dimmer = Dimmer()
print(dimmer.state.on, dimmer.state.value) # start state
dimmer.state.on = True
dimmer.state.value = 15
print(dimmer.state.on, dimmer.state.value) # state did not change
dimmer.state = State(True, 15)
print(dimmer.state.on, dimmer.state.value) # state changed
The first print is just to show the start state ("False 0"). But the second print shows that
dimmer.state.on = True
dimmer.state.value = 15
are useless. This is because dimmer.state returns a new mutable object which is modified and destroyed without further usage. Only through the complete property assignment the setter methode is called and Dimmer._send invoked.
I think this might be extremely unintuitive and error-prone. Do you have any suggestions for a better design?

Python type hints: Making generic type inference work with inheritance

Let's say I have some abstract Node class. Each Node should take some input data and produce output data, each of a potentially different type. To make development easier, to see what Node's are compatible with other Node's, I want to use type hints, so I have:
T_In = TypeVar("T_In")
T_Out = TypeVar("T_Out")
class Node(Generic[T_In, T_Out]):
input_: T_In
output: T_Out
def __init__(self, input_: T_In):
self.input_ = input_
self.output = None
def prepare_output(self):
raise NotImplemented()
def get_output(self) -> T_Out:
return self.output
So far so good (this is of course not meant to be directly instantiated).
Now I have a subclass MapNode that takes some function that does the actual data conversion. I want to use the type signature of the function to automatically determine the types for the Node class. More specifically, this node always takes a list of some type (equivalent to the function's input type), and produces a list of another type (function output type). What I tried is this:
T_Lambda_In = TypeVar("T_Lambda_In")
T_Lambda_Out = TypeVar("T_Lambda_Out")
New_IT = TypeVar("New_IT", bound=List[T_Lambda_In])
New_OT = TypeVar("New_OT", bound=List[T_Lambda_Out])
class MapNode(Node[New_IT, New_OT]):
def __init__(self, input_, lambda_: Callable[[T_Lambda_In], T_Lambda_Out]):
super(Node, self).__init__(input_)
self.lambda_ = lambda_
def prepare_output(self):
self.output = [self.lambda_(obj) for obj in self.input_]
A function could be, for instance:
def cast_int(val: str) -> int:
return int(val)
My thinking was that this should generate the type hints:
node: MapNode[List[str], List[int]] = MapNode(["1", "2"], lambda_=cast_int)
Instead, PyCharm generates:
node: MapNode[Any, Any] = MapNode(["1", "2"], lambda_=cast_int)
It seems that the usage of the types in the function signature is not enough to resolve the "derived" types T_Lambda_In and T_Lambda_Out correctly. Any help in fixing this would be appreciated! I'm using Python 3.6 by the way.

Categories

Resources