assign different value to python object - python

I want to make a python class named "Foo" that has an attribute called "x" which is an int with a default value of 0.
If I assign a posetive value to x it would automatically change to 10 and if I assign a negative value it would change it to -10. for example:
>>p = Foo()
>>print(p.x)
0
>>p.x = -45
>>print(p.x)
-10
>>p.x = 85
>>print(p.x)
10

Utilize the property decorator:
You first define _x as a class member in the constructor:
def __init__(self):
self._x = 0
and initialize it with 0.
Then you define x as property:
#property
def x(self):
return self._x
This means that we can get the values of _x through the name x.
Then you define setter for x with #x.setter:
#x.setter
def x(self, value):
self._x = 10 if value > 0 else (-10 if value < 0 else 0)
This overrides the original logics for setting value to x, with the one that you defined.
ATTENTION: _x is still accessible, and if you change it the magic defined above won't hold (there is no private class members such as in other languages like C, C++, C#, Java, etc...). Things that start with _ conventionally means that they are of internal use.
Code:
class Foo(object):
def __init__(self):
self._x = 0
#property
def x(self):
return self._x
#x.setter
def x(self, value):
self._x = 10 if value > 0 else (-10 if value < 0 else 0)
p = Foo()
# 0
print(p.x)
p.x = -45
# -10
print(p.x)
p.x = 85
# 10
print(p.x)

Related

When setting variable to object, set it to all nested objects within

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.

Why does my class variable _x don't increase?

I want to describe the class instances of which store quantity of instances of that class. Here is the code:
class WeAre:
_x = 0
def __init__(self):
self._x += 1
#property
def count(self):
return self._x
#count.setter
def count(self, val):
return None
#count.deleter
def count(self):
return None
def __del__(self):
self._x -= 1
a = WeAre()
print(a.count)
b, c = WeAre(), WeAre(),
a.count = 100500
print(a.count, b.count, c.count)
del b.count
del b
print(a.count)
Here is my output:
1
1 1 1
1
And I can't understand why class variable _x doesn't increase/decrease
In constructor you are creating instance's variable which overshadows class variable. Therefor you are always working with object instance's variable.
class WeAre:
_x = 0 # <-- class variable
# creates uses
# object var class var
#
# | |
def __init__(self): # V V
self._x += 1 # same as self._x = self._x + 1
That would work:
class WeAre:
_x = 0
def __init__(self):
WeAre._x += 1
But you may need to re-think it if you would need to override WeAre class.

is it OK to use property setter on private variables in python?

In the following code snippet, I use a setter on the x attribute (which I'd like to keep private)
class test:
def __init__(self, pos, x):
self._pos = pos
self.x = x # Want to be a private variable, eg. self._x
#property
def x(self):
return self._x
#x.setter
def x(self, x):
# Logic for setting x
if self._pos == 'long':
self._x = -1 * abs(x)
elif self._pos == 'short':
self._x = abs(x)
else:
raise ValueError('$$ pos must be long or short')
The problem is I end up with TWO attributes , self.x AND self._x . Since I want x to be private - I'd like to only have self._x (and discard self.x) . What's missing in the code ?

Is it true that once property is used in Python, it'd always be better initializing via property?

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.

Setting Values Of Inherited Properties

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?

Categories

Resources