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))
Related
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 "".
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)
I am coding something with python and I made a main class. I am working with tkinter.
In my class
class Main(tk.Tk):
I have multiple variables. There are two variables, which I defined. Underneath that, there is another variable, that runs the other two variables, I wrote above. But then it says, that those are not defined, but it is. The Error message: name 'bruteforceABC' is not defined
class Main(tk.Tk):
def bruteforceABC():
for length in range(1, 3): # only do lengths of 1 + 2
to_attempt = product(chars, repeat=length)
for attempt in to_attempt:
print(''.join(attempt))
def clear1():
list = window.grid_slaves()
for n in list:
n.destroy()
def clearforce():
bruteforceABC()
clear1()
I don't know, why it says, it is not defined. Because I've defined it. What can I do, that I don't get this error?
Thank you, for your help!
You have defined these functions as class methods but are calling them as generic ones. You should use self.method() to call them.
class Main(tk.Tk):
#staticmethod
def bruteforceABC():
for length in range(1, 3): # only do lengths of 1 + 2
to_attempt = product(chars, repeat=length)
for attempt in to_attempt:
print(''.join(attempt))
#staticmethod
def clear1():
list = window.grid_slaves()
for n in list:
n.destroy()
def clearforce(self):
self.bruteforceABC()
self.clear1()
Like this
every one I am practicing Python, and I found something strange, here is my code
LogicG.py
class LogicGate:
def __init__(self,n):
self.label = n
self.output = None
def getLabel(self):
return self.label
def getOutput(self):
self.output = self.performGateLogic()
return self.output
class BinaryGate(LogicGate):
def __init__(self,n):
LogicGate.__init__(self,n)
self.pinA = None
self.pinB = None
def getPinA(self):
return int(input("Enter Pin A input for gate "+ self.getLabel()+"-->"))
def getPinB(self):
return int(input("Enter Pin B input for gate "+ self.getLabel()+"-->"))
class UnaryGate(LogicGate):
def __init__(self,n):
LogicGate.__init__(self,n)
self.pin = None
def getPin(self):
return int(input("Enter Pin input for gate "+ self.getLabel()+"-->"))
class AndGate(BinaryGate):
def __init__(self,n):
super(AndGate,self).__init__(self,n)
def performGateLogic(self):
a = self.getPinA()
b = self.getPinB()
if a==1 and b==1:
return 1
else:
return 0
however, it shows the error below, I use python 3.6.4
after I mark out the code "the super" it can work ok
super(AndGate,self).init(self,n)
class AndGate(BinaryGate):
# def __init__(self,n):
# super(AndGate,self).__init__(self,n)
def performGateLogic(self):
a = self.getPinA()
b = self.getPinB()
if a==1 and b==1:
return 1
else:
return 0
above those code just I copy from this site for python study, see that site in Listing 11, it shows these code work, but when I copy to my computer the code did not work?? I have to mark out "super" part?? why? thank you
When using super, self is passed automatically.
Also, in Python3.3 and above, super does not even need to receive arguments to know from which class it is being called. You can simply do this.
super().__init__(n)
This greatly improves maintainability, so it would be the prefered approach.
If you are using Python 3.3 and above, you should replace
LogicGate.__init__(self,n)
with
super().__init__(n)
It is better to use this format when ever you want to call super class constructor.
You don't need to pass self here:
super(AndGate,self).__init__(self,n)
It should be
super(AndGate,self).__init__(n)
class Time:
def __init__(self,x,y,z):
self.hour=x
self.minute=y
self.second=z
def __str__(self):
return "({:02d}:{:02d}:{:02d})".format(self.hour, self.minute, self.second)
def time_to_int(time):
minutes=time.hour*60+time.minute
seconds=minutes*60+time.second
return seconds
def int_to_time(seconds):
time=Time()
minutes,time.second=divmod(seconds,60)
time.hour,time.minute=divmod(minutes,60)
return time
def add_time(t1,t2):
seconds=time_to_int(t1)+time_to_int(t2)
return int_to_time(seconds)
start=Time(9,45,00)
running=Time(1,35,00)
done=add_time(start,running)
print(done)
I am new to python and i've been doing some practice lately.I came across a question and i've written the code for the same.But I am repeatedly getting an error: "add_time is not defined". I tried defining a main() method but then it doesn't print anything.Please help.
You haven't created an object to the above class.
Any function/method inside a class can only be accessed by an object of that class .For more information on the fundamentals of Object Oriented Programming, please check this page.
Meanwhile for this to work, define your class in the following way :
class Time:
def __init__(self,x=None,y=None,z=None):
self.hour=x
self.minute=y
self.second=z
def __str__(self):
return "({:02d}:{:02d}:{:02d})".format(self.hour, self.minute, self.second)
def time_to_int(time):
minutes=time.hour*60+time.minute
seconds=minutes*60+time.second
return seconds
def int_to_time(seconds):
time=Time()
minutes,time.second=divmod(seconds,60)
time.hour,time.minute=divmod(minutes,60)
return time
def add_time(t1,t2):
seconds=time_to_int(t1)+time_to_int(t2)
return int_to_time(seconds)
and outside the class block, write the following lines :
TimeObject = Time()
start=Time(9,45,00)
running=Time(1,35,00)
TimeObject.add_time(start,running)
print "done"
I however suggest you to write the add_time function outside the class because you are passing the objects to the class as the parameters to the function within the same class and it is considered as a bad design in object oriented programming.
Hope it helps. Cheers!
This works fine for me as long as you specified 3 args in your constructor
def int_to_time(seconds):
time=Time(0,0,0) # just set your 3 positionals args here
minutes,time.second=divmod(seconds,60)
time.hour,time.minute=divmod(minutes,60)
return time
Another way to avoid it could be:
class Time:
def __init__(self,x=0,y=0,z=0):
self.hour=x
self.minute=y
self.second=z
If you want to add your functions to your class (such as time_to_int, int_to_time or even add_time) then you will need to indent with one more level of 4 spaces and add self to your method parameters
Hii Mathers25,
I solve your problem try this below code to get the best output,
class TimeClass:
def __init__(self,x,y,z):
self.hour = x
self.minute = y
self.second = z
def __str__(self):
return "({:02d}:{:02d}:{:02d})".format(self.hour, self.minute, self.second)
def time_to_int(self,time):
minutes = (time.hour * 60) + time.minute
seconds = (minutes * 60) + time.second
return seconds
def int_to_time(self,seconds):
time = TimeClass(0,0,0)
minutes,time.second=divmod(seconds,60)
time.hour,time.minute=divmod(minutes,60)
return time
def add_time(self,t1,t2):
seconds = self.time_to_int(t1) + self.time_to_int(t2)
# Call method int_to_time() using self keyword.
return self.int_to_time(seconds)
# First time object create that time set value is 0 of hour,minute and second
TimeObject = TimeClass(0,0,0)
# After create second object
start=TimeClass(9,45,00)
# After create thired Object
running=TimeClass(1,35,00)
# Store the value which return by add_time()
done = TimeObject.add_time(start,running)
# Display the value of done variable
print(done)
class Employee:
def __init__(self):
self.wage = 0
self.hours_worked = 0
def calculate_pay(self):
return self.wage * self.hours_worked
alice = Employee()
alice.wage = 9.25
alice.hours_worked = 35
print('Alice:\n Net pay: {:.2f}'.format(alice.calculate_pay()))
barbara = Employee()
barbara.wage = 11.50
barbara.hours_worked = 20
print('Barbara:\n Net pay: {:.2f}'.format(barbara.calculate_pay()))
Works for me:
class C:
def f(a, b):
return a + b
x = f(1,2)
print(C.x)
but you should not do such things. Code in class-level is executing when class is "creating", usually you want static methods or class methods (decorated with #staticmethod or #classmethod) and execute code in some function/instantiated class. Also you can execute it on top (module) level if this is the simple script. Your snippet is "bad practice": class level (i'm talking about indentation) is for declarations, not for execution of something. On class-level is normal to execute code which is analogue of C macros: for example, to call decorator, to transform some method/attribute/etc - static things which are "pure" functions!