I want to implement the move method in Shape class in has-a relation(So Shape class does not inherit CPoint class), but have a problem in setting class variables.
The given code is:
class CPoint:
def __init__(self, x = 0, y = 0):
self.__x = x
self.__y = y
def __str__(self):
return f"pos({self.__x},{self.__y})"
def get_x(self):
return self.__x
def set_x(self, new_x):
self.__x = new_x
def get_y(self):
return self.__y
def set_y(self, new_y):
self.__y = new_y
def move(self, a, b):
self.__x += a
self.__y += b
return CPoint(self.__x,self.__y)
class Shape:
def __init__(self, color = "yellow", filled = True, pos = CPoint()):
#So each pos must reference the different CPoint() instance
self.__color = color
self.__filled = filled
self.__pos = pos
def __str__(self):
return f"{self.__pos}({self.__color},{self.__filled})"
def move(self, a, b):
self.__pos.move(a,b)
if type(self) == Shape:
return f"{self}"
else:
return f"{self.__pos}{self}"
def main():
a = Shape()
b = Shape("red")
a.move(2,3)
print(b.move(4,5))
main()
the result is:
pos(0,0)(yellow,True)
pos(0,0)(red,True)
pos(2,3)(yellow,True)
pos(6,8)(red,True)
and the result should be like:
pos(0,0)(yellow,True)
pos(0,0)(red,True)
pos(2,3)(yellow,True)
pos(4,5)(red,True)
And I executed the code on the python tutor, and the visualization of the code is like this:
python tutor visualization
So Shape() and Shape("red") objects should reference different CPoint instance (cuz they have their own position data), but they reference the same instance even though I set the default parameter like 'pos = CPoint()'.
Can someone please explain why they're referencing the same instance, and how to get around it?
This is how python does argument defaults, default arguments are initialized once during function declaration and not every time when the function is called like you might expect if you've used other languages like Javascript https://docs.python-guide.org/writing/gotchas/#mutable-default-arguments
Because of this, the same CPoint() instance is shared between different constructor calls.
To avoid this behavior you could try setting it inside the function itself.
class Shape:
def __init__(self, color = "yellow", filled = True, pos = None):
if pos is None:
pos = CPoint()
#So each pos must reference the different CPoint() instance
self.__color = color
self.__filled = filled
self.__pos = pos
Related
I have the following code that works:
class fundamental_object():
def __init__(self,x):
self.x = x
class encapsulator_object():
def __init__(self,x,obj_list):
self._x = x
self.obj_list = obj_list
#property
def x(self):
return self.x
#x.setter
def x(self,new_x):
print('in setter!')
self._x = new_x
for obj in self.obj_list:
obj.x = new_x
if __name__ == '__main__' :
x = 10
obj_1 = fundamental_object(x)
obj_2 = fundamental_object(x)
obj_list = [obj_1,obj_2]
encapsulator = encapsulator_object(x,obj_list)
encapsulator.x = 20
print(encapsulator._x)
print(obj_1.x) # all of these are also updated to 20.
As you can see, the idea is that, whenever I change the attribute "x" of the encapsulator object, I want all nested objects inside it (fundamental_objects) to also be updated with this new variable "x". However, from a user standpoint, this gets confusing really quickly, since, if I understand correctly, "x" is an integer for "fundamental_object", whereas "x" is a method for "encapsulator_object", and to actually access the integer in the encapsulator I would need to use "_x". Is there any easy/correct/pythonic way to make it so the following works :
x = 10
obj_1 = fundamental_object(x)
obj_2 = fundamental_object(x)
obj_list = [obj_1,obj_2]
encapsulator = encapsulator_object(x,obj_list)
encapsulator.x = 20
print(encapsulator.x) # notice the underscore is now gone!
print(obj_1.x) # this would be updated to 20 as well
I understand that it is possible to make it so "fundamental_objects" also have "_x" as the integer variable, which would somewhat reduce the confusion, but still, I'd like to completely get rid of the underscores if possible! (right now I get an infinite recursion). Thanks!
Check this code. I only changed your getter method in your property. Now it is pointing to the self._x.
class fundamental_object():
def __init__(self, x):
self.x = x
class encapsulator_object():
def __init__(self, x, obj_list):
self._x = x
self.obj_list = obj_list
#property
def x(self):
return self._x # -----> here
#x.setter
def x(self, new_x):
print('in setter!')
self._x = new_x
for obj in self.obj_list:
obj.x = new_x
if __name__ == '__main__':
x = 10
obj_1 = fundamental_object(x)
obj_2 = fundamental_object(x)
obj_list = [obj_1, obj_2]
encapsulator = encapsulator_object(x, obj_list)
encapsulator.x = 20
print(encapsulator.x) # notice the underscore is now gone!
print(obj_1.x) # this would be updated to 20 as well
As an alternative you can completely remove x or _x in encapsulator_object. Then in your getter you can find x within the self.obj_list :
class fundamental_object():
def __init__(self, x):
self.x = x
class encapsulator_object():
def __init__(self, obj_list):
self.obj_list = obj_list
#property
def x(self):
return self.obj_list[0].x
#x.setter
def x(self, new_x):
print('in setter!')
for obj in self.obj_list:
obj.x = new_x
Bear in mind that, in this example because we decided to pick first item in the list, all objects must have the same x value. There is no need to worry about it after you can the setter though. I mentioned it if you want to call the getter before setter.
I'm following this code example from a python course:
class P:
def __init__(self,x):
self.x = x
#property
def x(self):
return self.__x
#x.setter
def x(self, x):
if x < 0:
self.__x = 0
elif x > 1000:
self.__x = 1000
else:
self.__x = x
And I tried to implement this pattern to my own code:
class PCAModel(object):
def __init__(self):
self.M_inv = None
#property
def M_inv(self):
return self.__M_inv
#M_inv.setter
def set_M_inv(self):
M = self.var * np.eye(self.W.shape[1]) + np.matmul(self.W.T, self.W)
self.__M_inv = np.linalg.inv(M)
Note that I want the M_inv property to be None before I have run the setter the first time. Also, the setter solely relies on other properties of the class object, and not on input arguments.
The setter decorator generates an error:
NameError: name 'M_inv' is not defined
Why is this?
Your setter method should be like below:
#M_inv.setter
def M_inv(self):
M = self.var * np.eye(self.W.shape[1]) + np.matmul(self.W.T, self.W)
self.__M_inv = np.linalg.inv(M)
The decorator #M_inv.setter and the function def M_inv(self): name should be same
The example is wrong.
EDIT: Example was using a setter in __init__ on purpose.
Getters and setters, even though they act like properties, are just methods that access a private attribute. That attribute must exist.
In the example, self.__x is never created.
Here is my suggested use :
class PCAModel(object):
def __init__(self):
# We create a private variable
self.__M_inv = None
#property
def M_inv(self):
# Accessing M_inv returns the value of the previously created variable
return self.__M_inv
#M_inv.setter
def M_inv(self): # Keep the same name than your propery
M = self.var * np.eye(self.W.shape[1]) + np.matmul(self.W.T, self.W)
self.__M_inv = np.linalg.inv(M)
I am currently playing around with classes and functions since i am not familiar with python and i would like to know how i can get addy(self, addx) to call addx.
class test:
def __init__(self, x):
self.x = x
def addx(self):
y = self.x + 10
return y
def addy(self, addx):
z = addx() + 10
return z
one = test(1)
print(one.addy())
line 15, in print(one.addy()) TypeError: addy() missing 1
required positional argument: 'addx' Process finished with exit code 1
You need to call self from within a class method.
self.addx()
Also the addx parameter on this line shouldn't be there:
def addy(self, addx):
I think this is what you are going for:
class test:
def __init__(self, x):
self.x = x
def addx(self):
y = self.x + 10
return y
def addy(self):
z = self.addx() + 10
return z
one = test(1)
print(one.addy())
You've overcomplicated things by wrapping it in a class. Take it out and it'll work (mostly) the way you expect.
def add10(x):
return x+10
def add20(x):
return add10(add10(x))
Since you've wrapped it in the class you've complicated the namespace. It's no longer called addx or addy, so using those names throws a NameError. You have to use the qualified name instead.
class FooBar():
def __init__(self):
self.x = 10
def addx(self):
return self.x + 10 # Note the `self.` before the attribute...
def addy(self):
return self.addx() + 10 # ...and also before the method name.
Methods are always passed their owning object as a first argument when called, which is why we've got def addx(self): but then call with self.addx()
If you are attempting to relate addx in the signature of addy to the method addx, you can pass the string name of the method and use getattr:
class Test:
def __init__(self, x):
self.x = x
def addx(self):
y = self.x + 10
return y
def addy(self, func):
z = getattr(self, func)() + 10
return z
s = Test(3)
print(s.addy('addx'))
Does anyone know how i can get the name of an object with a class function?
I want to use this because i have a class that is supposed to have multiple players in a list, and get their cordinates
object that wants to give their cordinates:
import math
class Pixel:
"""Represents a posision in a grid with a x posistion, a y position and
a character, the x and y position are saved in one tuple"""`
def __init__(self, char='#', pos=(0, 0)):
assert type(char) == str
assert type(pos[0]) == int and type(pos[1]) == int
self.pos = pos
self.x = self.pos[0]
self.y = self.pos[1]
self.char = char
def __str__(self):
return self.char
def __repr__(self):
return self.char
# possible debug repr 'Pixel object with
# char = ' + str(self.char) + ' and pos = ' + str(self.pos)`
class TestObject(Pixel):
def __str__(self, parent):
return '+'
The parent object wants to know what self.pos is from theTestObject (the parent has a list with different testObjects) is there a way for me to give the TestObject the name of the parent object when i am creating it so that he can push that information (parent.funcname(position)) I need it for a function that doesn't return values.
if anyone knows another way to get TestObject.pos to my parent object please tell too, thanks in advance
You could give your instances of TestObject a reference to the parent. This could be done via the __init__ method of TestObject. This way however, the parent has to be known at the time of the instance's construction. I would make it an optional (keyword) parameter and implement a setter method. For example:
class Pixel:
def __init__(self, char='#', pos=(0, 0), parent=None):
assert type(char) == str
assert type(pos[0]) == int and type(pos[1]) == int
self.pos = pos
self.x = self.pos[0]
self.y = self.pos[1]
self.char = char
self._parent = parent
def set_parent(self, parent):
self._parent = parent
class TestObject(Pixel):
def somemethod(self):
position = 0
# do what ever you need to do here
if isinstance(self._parent, Pixel):
self._parent.set_value(self)
When you add an instance obj of TestObject to the list in the parent object, the parent object should call obj.set_parent(self) to set itself as the object's parent.
Here is an example for a parent class:
class PixelList:
def __init__(self):
self._pixels = []
def add_pixel(self, pixel):
self._pixels.append(pixel)
pixel.set_parent(self)
def set_value(self, pixel):
# do stuff here
position = pixel.pos
If you then call obj.somemethod, your parent object's set_value method is called with the pixel as argument. In the set_value method you can thus access any information of the pixel you like (e.g. pixel.pos).
class Point(object):
def __init__(self, x=0, y=0):
self.__x = x
self.__y = y
print 'point ({x},{y}) created.'.format( x=self.__x, y=self.__y )
class Line(object):
def __init__(self, start_point=Point(), end_point=Point()):
print 'Line.__init__ called.'
self.start = start_point
self.end = end_point
def test_line():
p1 = Point(1,1)
p2 = Point(2,2)
line1 = Line(p1, p2)
if __name__=='__main__':
test_line()
When I run this script, it gives the result:
point (0,0) created.
point (0,0) created.
point (1,1) created.
point (2,2) created.
Line.__init__ called.
I don't know why Point.__init() is called before line1 is created.
def __init__(self, start_point=Point(), end_point=Point())
Default function parameters are members of the function itself and are created when the function is defined, not at runtime. So each instance of Line uses the same start and end
The common way to work around this is set them by default to None:
def __init__(self, start_point=None, end_point=None):
if start_point is None:
self.start = Point()
if end_point is None:
self.end = Point()