Properties that are more fun
Is it just me, or is the Python syntax for defining properties of classes a bit cumbersome? Like this:
class Example: @property def my_property(self): return self.real_value @my_property.setter def my_property(self, value): self.real_value = value
My main problem with this is that the name of the property,
my_propertyabove, has to be repeated 3 times. This does not seem very Pythonic, and is a pain when I want to rename the property, or want to create several properties - copy/paste or a snippet, then fix the name 3 times. People with editors that have better refactoring support might not mind so much, but in Pythonista it is painful.
So, I have started to routinely use a decorator like this:
def prop(func): return property(func, func)
With it, defining a new property goes like this:
class Example: @prop def my_property(self, *value): if value: self.real_value = value else: return self.real_value
A bit ugly, same amount of lines, but easy to copy/paste or refactor, as the name of the property is there only once.
I do not know if anyone else cares, but opinions and alternate approaches are very welcome.
I am not sure, but IIRC, you could also use
my_property = property(getter, setter)instead of the
@bennr01, while that syntax is maybe cleaner in a way, would I not still need to maintain 3 names – property, getter and setter?
@mikael You are right. This was only meant as a suggestion for reducing the number of times you have to write
my_property(for a complete getter/setter/deleter setup it would be 3 instead of 5 times). Your decorator
@propis indeed neat, but it may still be useful to remember the
my_property = property(...)syntax in case you (or anyone else who reads this) may find themself in a situation where you can not use it (due to code style guidelines or other reasons). I had not seen that you actually used this syntax in your example above and thought it may be a nice tip for you.
would I not still need to maintain 3 names – property, getter and setter?
I am not sure, but you may be able to use
lambdain this case, which could still be usefull for some cases like read-only properties. Also, in some cases the getter and setter may only be calling some other aliases (like getting/setting text labels).
Thanks for this great idea.
I have renamed your decorator to getter_setter, to make the purpose of it more clear.
Thus we define a decorator function:
def getter_setter(func): return property(func, func)
and then we can redo the example as:
@getter_setter def my_property(self, *value): if value: self._my_property = value else: return self._my_property
or EAFP style:
class Example: @getter_setter def my_property(self, *value): try: self._my_property = value except IndexError: return self._my_property
Optionally, we could define two more decorators for getter only and setter only properties:
def getter(func): def raise_attribute_error(*args): raise AttributeError("can't set attribute") return property(func, raise_attribute_error) def setter(func): def raise_attribute_error(*args): raise AttributeError("can't get attribute") return property(raise_attribute_error, func)
Here is an example of a Circle class demonstrating the three decorators.
radius can be used to get the radius and set the radius (thus updating the _area attribute)
area can be used to get the area only. Setting will raise an error.
diameter can only be used to set the radius. You can’t get it.
Here is an implementation with getter_setter only:
import math class Circle: def __init__(self, radius): self.radius = radius @getter_setter def radius(self, *value): if value: self._radius = value self._area = math.pi * self._radius ** 2 else: return self._radius @getter_setter def area(self, *value): if value: raise AttributeError("can't set attribute") else: return self._area @getter_setter def diameter(self, *value): if value: self.radius = value / 2 else: raise AttributeError("can't get attribute") And if we use getter and setter as well: import math class Circle: def __init__(self, radius): self.radius = radius @getter_setter def radius(self, *value): if value: self._radius = value self._area = math.pi * self._radius ** 2 else: return self._radius @getter def area(self): return self._area @setter def diameter(self, value): self.radius = value / 2
This post is deleted!last edited by
Property is useful when you have needs like validation, caching etc. and it is not much useful for simple cases. Hence I feel it is better to use standard methodology even if it is a bit cumbersome. See this for some good real life examples. https://stackoverflow.com/questions/6304040/real-world-example-about-how-to-use-property-feature-in-python
Using property to update variable in CustomView. (Position in GUI and equivalent value)
This GUI Customview is where the getter_setter would certainly be useful!