I am creating a python class out of raw data as follows:
Class Test:
def __init__(self, raw_number):
self._raw_number = raw_number
I am then computing _raw_number as a property to return the actual number I would like to have:
#property
def number(self):
return self._raw_number[0]
However, when trying to set this property on a Test object using
t = Test([1, 2, 3]) # this will set the number property to 1
t.number = 5 # this is supposed to set the number property to 5
I am running into an error that says property number cannot be set.
I am wondering why I can solve this?
#property by itself (at least, when used as shown) only provides a getter. Unless you provide a setter as well, the property is read-only.
class Test:
def __init__(self, raw_number):
self.number = raw_number
#property
def number(self):
return self._raw_number[0]
#number.setter
def number(self, value):
self._raw_number = value
Keep in mind that the setter should be responsible for ensuring that the value of _raw_number is, in fact, indexable. Also, the __init__ method can make use
of the setter in initializing the property; only the getter and setter themselves should be accessing the underlying _raw_number attribute.
property can be used to set the getter and setter simultaneously.
class Test:
def __init__(self, raw_number):
self.number = raw_number
def _getter(self):
return self._raw_number[0]
def _setter(self, value):
try:
value[0]
except IndexError:
raise TypeError("Value must be indexable")
self._raw_number = value
number = property(_getter, _setter)
# Clean up the namespace before creating the class
del _getter
del _setter
Related
I have main class with a lot of attributes that are initially defined as an object of a Prop class. This Prop class has two attributes: its value and the options, which is a list of acceptable values for the attribute.
class Prop():
def __init__(self, value, *options):
self.value = value
self.options = options
class Main():
def __init__(self):
self._prop1 = Prop(None)
self._prop2 = Prop(None)
The first important thing here is that _propx has to be an instance variable, since I will create more than one instance of Main.
The values of a Prop instance can either be a string or an integer, but the problem with this code is that I have to be sure that the user will do something like main._prop1.value = 1 and not main._prop1 = 1 otherwise it would break my code when doing _prop1.options. I don't want to use traits, thus I decided to make each _propx instance a kind of property, but I'm talking about a lot of instances and I don't want to define each setter especially because they will be all the same.
I found two solutions to solve this problem, the first is by using the same setter to all properties:
class Main():
def __init__(self):
self._prop1 = Prop(None)
self._prop2 = Prop(None)
def set_prop(attr):
def set_value(self, value):
self.__dict__[attr].value = value
return set_value
prop1 = property(fset=set_prop('_prop1'))
prop2 = property(fset=set_prop('_prop2'))
The second is by using an auxiliary class and redefine its __set__:
class Aux():
def __set_name__(self, owner, name):
self.public_name = name
self.private_name = '_' + name
def __set__(self, obj, value):
print(self, obj, value, self.private_name)
obj.__dict__[self.private_name].value = value
class Main():
def __init__(self):
self._prop1 = Prop(None)
self._prop2 = Prop(None)
prop1 = Aux()
prop2 = Aux()
the first on seems cleaner, but I have to pass the private name of each variable and I have to write the setter in the Main which I don't like because I would it to be as clean as possible. By other hand, in the second I have to use an auxiliary class.
My question is: is there a way of defining the setter in the Prop class? The reason why I couldn't find a way of doing this is that the Aux.__set__ seems to work only when I create an Aux instance as a class variable (static variable). This is also why I have to create a private and a public variable for each property. Is there a way of using __set__ to an instance (non-static) variable?
Take a look at this code snippet:
class Face():
pass
class Cube():
def __init__(self):
self.faces = {
'front': Face(1),
...
}
#property
def front(self):
return self.faces['front']
#front.setter
def front(self, f):
pass
I've created getters and setters for all the faces. Is there any way to make this code more compact, maybe by dynamically creating the getters and setters?
The following code assumes that you
have a reason to have the self.faces dict instead of setting attributes like front directly on the instance
and/or want to implement some meaningful getter and setter logic for the keys in self.faces.
Otherwise, this exercise is pretty pointless because as Corentin Limier noted you can simply set self.front = Face(1), and so on.
You can use descriptors, a class variable holding the face names and a class decorator. Think of descriptors as reusable properties.
In the following sample code I added a num instance variable to Face and the face 'side' just for demonstration purposes.
class FaceDescriptor:
def __get__(self, instance, owner):
# your custom getter logic
# dummy implementation
if instance is not None:
return instance.faces[self.face]
def __set__(self, instance, value):
# your custom setter logic
# dummy implementation
instance.faces[self.face] = value
def set_faces(cls):
for face in cls._faces:
desc = FaceDescriptor()
desc.face = face
setattr(cls, face, desc)
return cls
class Face():
def __init__(self, num):
self.num = num
#set_faces
class Cube():
_faces = ['front', 'side']
def __init__(self):
self.faces = {face:Face(i) for i, face in enumerate(self._faces, 1)}
In action:
>>> c = Cube()
>>> c.front.num
1
>>> c.side.num
2
>>> c.front = 'stuff'
>>> c.front
'stuff'
>>> c.faces
{'front': 'stuff', 'side': <__main__.Face at 0x7fd0978f37f0>}
Assuming that's all your class does, you could do something like
class Cube:
...
def __getattr__(self, name):
return self.faces[name]
def __setattr__(self, name, value):
self.faces[name] = value
if you really want to do that you could use __getattr__ and __setattr__:
class Cube:
...
def __getattr__(self, item):
return self.faces[item]
def __setattr__(self, item, value):
self.faces[item] = value
but as you set front in the __init__ methoud you could just as well make it a regular member...
Your code is redundant, since instance attributes are already stored in a dictionary which is the __dict__ property. I recognize that you are focused on writing your code in fewer lines. It is a good challenge to keep yourself growing, but in the long term you should be focused on the clarity of your code instead.
Here is a simpler way to write your code without using properties:
class Face():
pass
class Cube():
def __init__(self):
self.front = Face()
self.rear = Face()
It is a tenet of encapsulation that you should hide your "attributes" behind "properties". Even though this isn't strongly enforced in python, it's not a bad idea to do that. Here's the proper way to do that:
class Face():
pass
class Cube():
def __init__(self):
self._front = Face()
#property
def front(self):
return self._front
#front.setter
def front(self, value):
self._front = value
To answer your question at the end, yes you can dynamically create properties.
https://stackoverflow.com/a/1355444/3368572
But keep in mind that writing dynamic code should be reserved for special cases since it will make it more difficult for your IDE to follow the flow of your program. If you use the conventions as they are intended then your code becomes self-explanatory to people and to your IDE.
If I'm creating a class that needs to store properties, when is it appropriate to use an #property decorator and when should I simply define them in __init__?
The reasons I can think of:
Say I have a class like
class Apple:
def __init__(self):
self.foodType = "fruit"
self.edible = True
self.color = "red"
This works fine. In this case, it's pretty clear to me that I shouldn't write the class as:
class Apple:
#property
def foodType(self):
return "fruit"
#property
def edible(self):
return True
#property
def color(self):
return "red"
But say I have a more complicated class, which has slower methods (say, fetching data over the internet).
I could implement this assigning attributes in __init__:
class Apple:
def __init__(self):
self.wikipedia_url = "https://en.wikipedia.org/wiki/Apple"
self.wikipedia_article_content = requests.get(self.wikipedia_url).text
or I could implement this with #property:
class Apple:
def __init__(self):
self.wikipedia_url = "https://en.wikipedia.org/wiki/Apple"
#property
def wikipedia_article_content(self):
return requests.get(self.wikipedia_url).text
In this case, the latter is about 50,000 times faster to instantiate. However, I could argue that if I were fetching wikipedia_article_content multiple times, the former is faster:
a = Apple()
a.wikipedia_article_content
a.wikipedia_article_content
a.wikipedia_article_content
In which case, the former is ~3 times faster because it has one third the number of requests.
My question
Is the only difference between assigning properties in these two ways the ones I've thought of? What else does #property allow me to do other than save time (in some cases)? For properties that take some time to assign, is there a "right way" to assign them?
Using a property allows for more complex behavior. Such as fetching the article content only when it has changed and only after a certain time period has passed.
Yes, I would suggest using property for those arguments. If you want to make it lazy or cached you can subclass property.
This is just an implementation of a lazy property. It does some operations inside the property and returns the result. This result is saved in the class with another name and each subsequent call on the property just returns the saved result.
class LazyProperty(property):
def __init__(self, *args, **kwargs):
# Let property set everything up
super(LazyProperty, self).__init__(*args, **kwargs)
# We need a name to save the cached result. If the property is called
# "test" save the result as "_test".
self._key = '_{0}'.format(self.fget.__name__)
def __get__(self, obj, owner=None):
# Called on the class not the instance
if obj is None:
return self
# Value is already fetched so just return the stored value
elif self._key in obj.__dict__:
return obj.__dict__[self._key]
# Value is not fetched, so fetch, save and return it
else:
val = self.fget(obj)
obj.__dict__[self._key] = val
return val
This allows you to calculate the value once and then always return it:
class Test:
def __init__(self):
pass
#LazyProperty
def test(self):
print('Doing some very slow stuff.')
return 100
This is how it would work (obviously you need to adapt it for your case):
>>> a = Test()
>>> a._test # The property hasn't been called so there is no result saved yet.
AttributeError: 'Test' object has no attribute '_test'
>>> a.test # First property access will evaluate the code you have in your property
Doing some very slow stuff.
100
>>> a.test # Accessing the property again will give you the saved result
100
>>> a._test # Or access the saved result directly
100
I still don't fully understand when and how to use properties. Here I have a class SpecialCar which is inheriting Car. The variable summer_tire should basically be equivalent to tire, except for the name. So whenever I am asking for either of those two, I want to get summer_tire.
Using #property results in an error. Deleting the #property line will print 0, but I want to get 2.
class Car():
def __init__(self):
self.tire = 0
class SpecialCar(Car):
def __init__(self):
Car.__init__(self)
self.summer_tire = 2
self.winter_tire = 5
#property
def tire(self):
return self.summer_tire
i = SpecialCar()
print(i.tire)
You declared a property that doesn't have a setter, thus self.tire = 0 in Car.__init__ fails.
You could give your new property a setter:
class SpecialCar(Car):
def __init__(self):
Car.__init__(self)
self.summer_tire = 2
self.winter_tire = 5
#property
def tire(self):
return self.summer_tire
#tire.setter
def tire(self, new_tire):
self.summer_tire = new_tire
or you could avoid calling Car.__init__ altogether, or make Car.tire a class attribute, set as part of the class and replaced with the property in subclasses.
Trying to get my head around property decorators. I found a solution posted for setting read-only attributes here. Setting a private attribute and then providing a #property getter method makes sense if you can specify the attribute in init. But what about the case where you want to use a function to calculate a read-only attribute? Let's say you have a class that calls an attribute (e.g. state) from another class and then calculates a new value that will be made available as an attribute:
class MyState(object):
def __init__(self, starting_value):
self._foo = starting_value
#property
def foo(self):
return self._foo
#foo.setter
def foo(self, value):
self._foo = value
class MyClass(object):
def __init__(self, name=None):
self.name = name
#property
def bar(self):
state = MyState.foo
return id(state)
>mystate = MyState('chuff')
>myclass = MyClass()
>myclass.bar = 183097448L
In everything I have seen about property decorators, I have only see display methods reflected in the #property getter function, never functions that set the value of the variable. However, from reading the docs my understanding is that #setter requires an argument, which I don't have in this case. Is there any problem with calculating the read-only value of a class attribute in the #property getter method as opposed to simply passing an attribute that already exists?
There is no problem. #property is just doing less than you think. All it is is a bit of syntactic sugar to replace: a = foo.x with a = foo.x.getter(), and foo.x = bar with foo.x.setter(bar). That is, it allows you to replace attribute access with method calls. Those methods are allowed to do anything they like, which is the purpose of the property. I think you were being led astray by your first example where the property just passes through to an underlying hidden variable to make a psuedo-read-only variable. That is not really the standard use case. A very common example might be:
class Rectangle(object):
def __init__(self, w, h):
self.w = w
self.h = h
#property
def area(self):
return self.w * self.h
Area is a property of a rectangle, but it is derived from the width and height, and setting it doesn't really make any sense.