Python method call from class to class - python

I'm learning Python and I'm getting confused with syntax for calls from one class to another. I did a lot of search, but couldn't make any answer to work. I always get variations like:
TypeError: __init__() takes exactly 3 arguments (1 given)
Help much appreciated
import random
class Position(object):
'''
Initializes a position
'''
def __init__(self, x, y):
self.x = x
self.y = y
def getX(self):
return self.x
def getY(self):
return self.y
class RectangularRoom(object):
'''
Limits for valid positions
'''
def __init__(self, width, height):
self.width = width
self.height = height
def getRandomPosition(self):
'''
Return a random position
inside the limits
'''
rX = random.randrange(0, self.width)
rY = random.randrange(0, self.height)
pos = Position(self, rX, rY)
# how do I instantiate Position with rX, rY?
room = RectangularRoom()
room.getRandomPosition()

You don't need to pass self - that is the newly created instance, and is automatically given by Python.
pos = Position(rX, rY)
Note that the error here is happening on this line, however:
room = RectangularRoom()
The issue on this line is you are not giving width or height.

Maybe the answers to these previous questions help you understand why python decided to add that explicit special first parameter on methods:
What is the purpose of self?
Why do you need explicitly have the "self" argument into a Python method?
Python - why use "self" in a class?
The error message may be a little cryptic but once you have seen it once or twice you know what to check:
Did you forgot to define a self/cls first argument on the method?
Are you passing all the required method arguments? (first one doesn't count)
Those expecting/given numbers help a lot.

Related

pass arguments inside splitted methods

I need to split class methods in several files. Functionality need to by that I can pass inside method all variables defined in self and receive new self variables defined inside the method.
My attempt:
Below code works, but I don't know if this is the best/proper solution.
Base:
from calculate_function import function
class Data():
def __init__(self):
self.y = -2
self.x = 1
self.z, self.result = function(self)
calculate_function.py:
def function(self):
z = 2
result = z + self.x
return z, result
For above I pass self inside new function for collect all init variables, then define new self variable/results.
There will by much more functions inside different files that will done some calculations and create new variables for instance of class.
Question
What I need is to pass each created self variable to each function.
For above code the solution is proper defined or there is better option to this?
If you want to externalize some part of your class code to external functions, it's better to write those as pure functions and keep the attribute access (and even more attributes updates) within the class code itself - this makes the code much easier to test, read and maintain. In you case this would looks like:
from calculate_function import function
class Data():
def __init__(self):
self.y = -2
self.x = 1
self.z, self.result = function(self.x)
calculate_function.py:
def function(x):
z = 2
result = z + x
return z, result
The points here are that 1/ you can immediatly spot the creation of attributes z and result and 2/ you can test function() without a Data instance.
I need to split class methods in several files.
This often means your class has too many responsabilities. Some parts of it can be delegated to pure functions like shown above. Some other parts, that need access to a common subset of your class attributes, can be delegated to other, smaller, specialized classes - but preferably using composition / delegation instead of inheritance (depending on concrete use cases of course).
You dont need pass self inside the function
Why not do it like this:
class Data():
def __init__(self):
self.y = -2
self.x = 1
self.function()
def function(self):
self.z = 2
self.result = self.z + self.x
Do wish to use another Class function or just a stand alone function?
Here is solution, using class inheritance:
-- function1.py --
class FunctionClass1():
def function1(self):
self.result = self.x + self.y
-- function2.py --
class FunctionClass2():
def function2(self):
self.result = self.result + self.z
-- data.py --
from function1 import FunctionClass1
from function2 import FunctionClass2
class Data(FunctionClass1, FunctionClass2):
def __init__(self):
self.x = 1
self.y = 2
self.z = 3
self.function1()
self.function2()

Python: Am I using "self" correctly?

I'm new to Python and am only now starting to see the uses of self and would like to know if I am using it correctly. I have some sample code below and was wondering if someone could just skim through and see if it's the correct usage. I'm not sure if it's correct because I seem to be needing to use self a lot but perhaps that is just the style of the language. Thank You
Code
from tkinter import Canvas
class BouncyBall:
def __init__(self):
self.x = 0
self.y = 0
self.d = 15
self.color = 'blue'
self.speed = 2
self.move_left = False
self.move_right = False
def __init__(self, x, y, d, color):
self.x = x
self.y = y
self.d = d
self.color = color
self.speed = 2
self.move_left = False
self.move_right = False
#Accessor Methods
def get_x(self):
return self.x
def get_y(self):
return self.y
def get_diameter(self):
return self.d
def get_color(self):
return self.color
def get_speed(self):
return self.speed
def moving_right(self):
return self.move_right
def moving_left(self):
return self.move_left
#Mutator Methods
def set_x(self, x):
self.x = x
def set_y(self, y):
self.y = y
def set_diameter(self, d):
self.d = d
def set_color(self, color):
self.color = color
def set_speed(self, speed):
self.speed = speed
def set_move_right(self, move_right):
self.move_right = move_right
def set_move_left(self, move_left):
self.move_left = move_left
def draw_ball(self, canvas):
if isinstance(canvas, Canvas):
canvas.create_oval(self.x, self.y, self.x + self.d, self.y + self.d, fill=self.color)
else:
print("Improper Parameter Sent In")
You are using self correctly. It does tend to appear a lot in Python compared with other languages like C++ where the this parameter is implicit.
However, in Python it is not conventional to write get() and set() methods for everything, as you have done. You can reduce your code quite a bit by removing those--and remove a lot of selfs in the process.
Well, first off your __init__ is wrong. Python doesn't allow two definitions of a function/method with the same name in the same namespace, whether or not the prototype differs. I'd suggest dropping your first definition, and changing the def line of the second to:
def __init__(self, x=0, y=0, d=15, color='blue'):
which will do what you wanted (allow you to initialize without arguments by using default values).
You also probably want to drop all your set_ and get_ methods. If the attributes are read/write, just access them normally without getters and setters. If at some later point you need to make them read-only, or compute them, you can rename the attribute to have a leading underscore (e.g. _x) and use #property decorators to continue providing attribute-like access (with or without writability). That would instantly remove the vast majority of (unnecessary) accessor and mutator methods that make you reference self so often. For example, if x should be read-only, you'd set self._x = x in your __init__, then define a property:
#property
def x(self):
return self._x
and users would continue to read it as if it were a simple attribute, they just couldn't write it by accident (they could directly write _x, but that's their problem; Python's philosophy is that we're all adults, and if you ignore the convention that underscore prefixes are internal implementation details, the consequences are on your head).
Otherwise, yes, you'll be referencing self a lot. Python prefers explicit namespacing to implicit, so you use self to differentiate between instance access and scoped variable access. If you are going to be using a given variable a lot (and it won't change during the course of a method), you can cache it to a local name and use the local name unqualified, e.g.:
def some_method(self):
# We use x a lot and never change it, so cache up front:
x = self.x
# Can read cached x over and over without qualification for rest of method

Python Polymorphism

I have an assignment from an instructor trying to teach OOP in python. I'm pretty familiar with OOP in C++ and C# but having a difficulty figuring out what is going on in some of my code. It works and both classes function as I want but I had to add some weird code for it to work and I don't understand why.
Specifically referencing the code starting in line 64
class Cone(Cylinder):
#Constructor
def __init__ (self, radius, height):
Cylinder.__init__(self, radius, height)
self.calcVolume()
def calcVolume(self):
Cylinder.calcVolume(self)
self.__volume = Cylinder.GetVolume(self) * (1.0/3.0)
So when implementing Cone I don't understand why the Cylinder.calcVolume() is not called when the cone's constructor calls the cylinder's constructor. The code would make more sense to me if it was implicitly called but im forced to call the method explicitly. Some guidance or explanation would be awesome.
After Posting I made this change does it make more sense?
class Cone(Cylinder):
#Constructor
def __init__ (self, radius, height):
Cylinder.__init__(self, radius, height)
self.calcVolume()
def calcVolume(self):
self.__volume = self.GetBase().GetArea() * self.GetHeight() * (1.0/3.0)
This is what happens when you call Cone.__init__():
it performs Cylinder.__init__(),
in turns it calls self.calcVolume(),
because of inheritance, the resolution order finds the method on the Cone type,
it calls Cone.calcVolume() instead of Cylinder.calcVolume().
During __init__() I think you want to call:
Cone.calcVolume(self) in Cone.__init__(), or
Cylinder.calcVolume(self) in Cylinder.__init__().
Of course, if you were using new style classes (inheriting from object) then you could just use type(self).calcVolume(self); however type(self) on an old-style class is going to give you the instance type instead of the actual class, which isn't going to work in your case.
full example:
class Circle():
#Constructor
def __init__ (self, radius):
self.__radius = radius
self.calcArea()
def calcArea(self, PI = 3.14):
self.__area = (self.__radius**2) * PI
#Get Functions
def GetArea(self):
return self.__area
def GetRadius(self):
return self.__radius
#Set Functions
def SetRadius(self, radius):
self.__radius = radius
self.calcArea()
class Cylinder():
#Constructor
def __init__(self, radius, height):
self.__height = height
self.__base = Circle(radius)
Cylinder.calcVolume(self)
def calcVolume(self):
self.__volume = self.__base.GetArea() * self.__height
#Get Functions
def GetVolume(self):
return self.__volume
def GetBase(self):
return self.__base
def GetRadius(self):
return self.__base.GetRadius()
def GetHeight(self):
return self.__height
#Set Functions
def SetRadius(self, radius):
self.__base.SetRadius(radius)
self.calcVolume()
def SetHeight(self, height):
self.__height = height
self.calcVolume()
class Cone(Cylinder):
#Constructor
def __init__ (self, radius, height):
Cylinder.__init__(self, radius, height)
Cone.calcVolume(self)
def calcVolume(self):
Cylinder.calcVolume(self)
self.__volume = Cylinder.GetVolume(self) * (1.0/3.0)
#Get Functions
def GetVolume(self):
return self.__volume
#Set Functions
def SetRadius(self, radius):
Cylinder.SetRadius(self, radius)
self.calcVolume()
def SetHeight(self, height):
Cylinder.SetHeight(self, height)
self.calcVolume()
def main():
cylinder = Cylinder(5, 6)
cone = Cone(5, 6)
circle = Circle(5)
print cylinder.GetVolume()
print cone.GetVolume()
print circle.GetArea()
cone.SetHeight(7)
print cone.GetVolume()
main()
Rule is pretty straightforward: when the method is to be called, its name is resolved according to Method Resolution Order (it is tricky, as Python has multiple inheritance).
After the name is resolved, Python stops searching (I am simplifying). It is up to the specific method to call parent class's method (or any other method). Doing it implicitly could be a common cause of problems (eg. when you want to override some method from parent class).
Also, to invoke parent method use super(). Instead of writing this in Cone class:
Cylinder.__init__(self, radius, height)
write this:
super(Cone, self).__init__(self, radius, height)
That makes it more flexible: it just delegates finding proper method to MRO, which then checks parent classes in specific order (in this case, it will indeed find Cylinder.__init__).
More reading:
Guido Van Rossum on Method Resolution Order,
documentation of super() in Python 2.7,
"Python's super() considered super" - blog post about capabilities of super(),

Python: confused with classes, attributes and methods in OOP

I'm learning Python OOP now and confused with somethings in the code below.
Questions:
def __init__(self, radius=1):
What does the argument/attribute "radius = 1" mean exactly?
Why isn't it just called "radius"?
The method area() has no argument/attribute "radius".
Where does it get its "radius" from in the code?
How does it know that the radius is 5?
class Circle:
pi = 3.141592
def __init__(self, radius=1):
self.radius = radius
def area(self):
return self.radius * self.radius * Circle.pi
def setRadius(self, radius):
self.radius = radius
def getRadius(self):
return self.radius
c = Circle()
c.setRadius(5)
Also,
In the code below, why is the attribute/argument name missing in the brackets?
Why was is not written like this: def __init__(self, name)
and def getName(self, name)?
class Methods:
def __init__(self):
self.name = 'Methods'
def getName(self):
return self.name
The def method(self, argument=value): syntax defines a keyword argument, with a default value. Using that argument is now optional, if you do not specify it, the default value is used instead. In your example, that means radius is set to 1.
Instances are referred to, within a method, with the self parameter. The name and radius values are stored on self as attributes (self.name = 'Methods' and self.radius = radius) and can later be retrieved by referring to that named attribute (return self.name, return self.radius * self.radius * Circle.pi).
I can heartily recommend you follow the Python tutorial, it'll explain all this and more.
def __init__(self, radius=1):
self.radius = radius
This is default value setting to initialize a variable for the class scope.This is to avoid any garbage output in case some user calls c.Area() right after c = Circle().
In the code below, why is the attribute/argument "name" missing in the brackets?
In the line self.name = 'Methods' you are creating a variable name initialized to string value Methods.
Why was is not written like this: def init(self, name) and def
getName(self, name)?
self.name is defined for the class scope. You can get and set its value anywhere inside the class.
The syntax radius = 1 specifies a parameter "radius" which has a default value of 1:
def my_func(param=1):
... print(param)
...
my_func() #uses the default value
1
my_func(2) #uses the value passed
2
Note that in python there exists more kinds of parameters: positional and keyword parameters, or both.
Usually parameters can be assigned both using the positional notation and the keyword:
>>> def my_func(a,b,c):
... print (a,b,c)
...
>>> my_func(1,2,3)
(1, 2, 3)
>>> my_func(1,2,c=3)
(1, 2, 3)
Python uses "explicit" instance passing, so the first self parameter is used to pass the instance on which the methods are called. You can think of self as being the this of Java. But you must always use it to access instance attributes/methods. You can't call just area(), you must say self.area().
When you do self.attribute = 1 you create a new attribute attribute with value 1 and assign it to the instance self. So in the area() method self.radius refers to the radius attribute of the self instance.
The __init__ method is a special method. It's something similar to a constructor.
It is called when you instantiate the class. Python has a lot of these "special methods", for example the method __add__(self, other) is called when using the operator +.

python's logical error

i've written this simple code, i think that it doesn't contain gramatical error but it doesn't execute so i think that thare is somewhere a logical error :o, so please i need help !! someone can save me ?! :D
class NumberGenerator:
"""A simple class that contains functions to generate ranges of numbers"""
#classmethod
def generate(quantity, value=[]):
while quantity: value.append(quantity) ; quantity -= 1
return value
class Figure:
"""Abstract class for geometric figures"""
def init(self, name):
"""This is the constructor"""
self._name = name
def name(self):
return self._name
class Rectangle(Figure):
"""Rectangle figure"""
def init(self, width, height):
Figure.__init__("rectangle")
self._width = width
self._height = height
def width(self):
return self.width
def height(self):
return self.height
def size(self):
self.width() * self.height()
if __name__ == "__main__":
# We print the range(10,0,-1)
print NumberGenerator.generate(10)
# We print the range(20,0,-1)
print NumberGenerator.generate(20)
# We create a rectangle
r = Rectangle(10, 20)
print r.size()
# EOF
Indentation aside, two errors are right here:
def width(self):
return self.width
def height(self):
return self.height
This should read:
def width(self):
return self._width
def height(self):
return self._height
Also, constructors should be called __init__ and not init.
Finally, the generate() method has several issues:
You probably meant to say #staticmethod and not #classmethod (class methods should take an implicit first argument, which your method doesn't).
The loop is unnecessary (a simple range() call can do the trick).
The method has a subtle bug in it, whereby the second argument (value) is preserved across calls to generate(). If you must extend value, move value = [] into the body of the method.
Your first error message is AttributeError: 'int' object has no attribute 'append' from NumberGenerator.generate due to no self as the first argument and it is then creating the variable quantity as an instance of NumberGenerator.
Once you fix that, your next error is in the fact that init() should actually be __init__(), and once you fix that you have a further error, but I will leave that exercise to yourself.

Categories

Resources