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.
Related
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'))
I used to initialize private attributes in __init__ like below (this way of initializing is also commonly seen),
class Duck():
def __init__(self, input_name):
self.__name = input_name
#property
def name(self):
return self.__name
#name.setter
def name(self, input_name):
self.__name = input_name
# Use private attribute __name internally for other purposes below...
But I just want to make sure if it is actually safer to just use property at the very beginning __init__, for example, in next example, for input greater than 1000 or less than 0, I want to evaluate to 1000 and 0, respectively, rather than the original input value. It seems I can't use self.__x = x,
class P:
def __init__(self,x):
# If self.__x = x, not desirable
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
I assume you work with python2. Properties are not supported for old-style classes. Just change the first line to
class P(object):
And whether you use self._x or self.__x for the attribute behind does not matter. Just do it consistent, i.e. change the constructor line back to
self.__x = x
Just don't call that self.x as the property.
Edit:
There was a misunderstanding I think. Here the complete code I propose:
class P(object):
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
p = P(3)
p.x = 1001
print p.x
Edit 2 - The actual question:
I apologize, did simply not grasp the heading and actual question here, but was focused on making your class work. My distraction was that if you are in python2 and use old-style classes, the setter would not really get called.
Then like indicated in the comment-conversation below, I don't have a definite answer on whether to initialize the attribute or the property, but I (personally) would say:
a. If the initialisation deserves the same validation as performed in
the setter, then use the property, as else you'd need to copy the
setter code.
b. If however the value to initialise doesn't need validation (for
instance, you set it to an a priori validated constant default
value), then there is no reason to use the property.
I want to be able to create a concrete instance of a class that inherits from another concrete class, which in turn inherits from an abstract class.
The basic pattern is:
from abc import ABCMeta, abstractproperty
class Foo(object):
__metaclass__ = ABCMeta
#abstractproperty
def x(self):
pass
#abstractproperty
def y(self):
pass
class Bar(Foo):
x = None
y = None
def __init__(self, x, y):
self.x = x
self.y = y
#property
def x(self):
return self.x
#x.setter
def x(self, value):
self.x = value
#property
def y(self):
return self.y
#y.setter
def y(self, value):
self.y = value
class Baz(Bar):
def __init__(self):
super().__init__(x=2, y=6)
a = Baz()
When I try to create the instance of Baz I get a RecursionError: maximum recursion depth exceeded error. (As well as a pylint warning telling me that the signatures of the setter methods don't match the signatures of the base class)
However, if I remove the setters, I get an error self.x = x AttributeError: can't set attribute
What's the correct pattern to do this?
You need to change names for your x() / y() methods or for your x / y properties, for example rename
class Bar(Foo):
x = None
y = None
To:
class Bar(Foo):
x_val = None
y_val = None
And rename the references to x / y as well.
What you did is basically:
def x():
return x()
It happened because your def x overridden the x = None, so x is a function(property) that is calling itself. Avoid this by using another attribute(named differently) for storing the actual value of x.
Example from python docs (https://docs.python.org/3.5/library/functions.html#property):
class C:
def __init__(self):
self._x = None
#property
def x(self):
return self._x
#x.setter
def x(self, value):
self._x = value
Note: attribute names starting with underscore should be considered "private" and should not be directly accessed outside of the class. But it's only a convention for programmers, technically they are just normal attributes and you can do whatever you want, but it's nice to follow some convention, isn't it?
I'm having trouble on how to implement property to protect attributes.
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def set_x(self, x):
if '_x' in dir(self):
raise NotImplementedError("Cannot change x coordinate")
else:
self._x = x
def get_x(self):
return self._x
#I beleive my mistake is here. I'm not sure if I'm implementing this correctly
x = property(get_x, set_x, None, None)
So I want to prevent any user from changing the x-coordinate. My question is, how do I get python to redirect the user to the set_x() and get_x() methods? I've tried running this code in terminal and whenever I apply the following, the point changes.
p = point(3, 4)
p.x = 5 #x is now 5
You only need this much:
class Point:
def __init__(self, x, y):
self._x = x
self.y = y
def get_x(self):
return self._x
x = property(get_x)
You can set the hidden field self._x in your init, then you don't need a setter for x at all. And have get_x return self._x rather than self.x so it doesn't try and call itself.
You can use the #property decorator to do this even more succinctly.
class Point:
def __init__(self, x, y):
self._x = x
self.y = y
#property
def x(self):
return self._x
The following code works on both python2.x and python3.x:
class Point(object):
def __init__(self, x, y):
self.x = x
self.y = y
def set_x(self, x):
if '_x' in dir(self):
raise NotImplementedError("Cannot change x coordinate")
else:
self._x = x
def get_x(self):
return self._x
x = property(get_x, set_x, None, None)
p = Point(2, 3)
print(p.x) # 2
p.x = 6 # NotImplementedError
Pretty much all I did was inherit from object (to get it to work on python2.x) and use the name Point rather than point (which would have been a NameError before).
There are other things you can do to clean it up a bit (e.g. khelwood's suggestion of just writing the getter -- or DSM's suggestion of using hasattr instead of '_x' in dir(self)).
Note, if you really just want a type that takes an x and y arguments that you want to be immutable -- Maybe you should consider using a colledctions.namedtuple
from collections import namedtuple
Point = namedtuple('Point', 'x,y')
p = Point(2, 3)
p.x # 2
p.y # 3
p.x = 6 # AttributeError: can't set attribute