Related
I would like to understand how the built-in function property works. What confuses me is that property can also be used as a decorator, but it only takes arguments when used as a built-in function and not when used as a decorator.
This example is from the documentation:
class C:
def __init__(self):
self._x = None
def getx(self):
return self._x
def setx(self, value):
self._x = value
def delx(self):
del self._x
x = property(getx, setx, delx, "I'm the 'x' property.")
property's arguments are getx, setx, delx and a doc string.
In the code below property is used as a decorator. The object of it is the x function, but in the code above there is no place for an object function in the arguments.
class C:
def __init__(self):
self._x = None
#property
def x(self):
"""I'm the 'x' property."""
return self._x
#x.setter
def x(self, value):
self._x = value
#x.deleter
def x(self):
del self._x
How are the x.setter and x.deleter decorators created in this case?
The property() function returns a special descriptor object:
>>> property()
<property object at 0x10ff07940>
It is this object that has extra methods:
>>> property().getter
<built-in method getter of property object at 0x10ff07998>
>>> property().setter
<built-in method setter of property object at 0x10ff07940>
>>> property().deleter
<built-in method deleter of property object at 0x10ff07998>
These act as decorators too. They return a new property object:
>>> property().getter(None)
<property object at 0x10ff079f0>
that is a copy of the old object, but with one of the functions replaced.
Remember, that the #decorator syntax is just syntactic sugar; the syntax:
#property
def foo(self): return self._foo
really means the same thing as
def foo(self): return self._foo
foo = property(foo)
so foo the function is replaced by property(foo), which we saw above is a special object. Then when you use #foo.setter(), what you are doing is call that property().setter method I showed you above, which returns a new copy of the property, but this time with the setter function replaced with the decorated method.
The following sequence also creates a full-on property, by using those decorator methods.
First we create some functions and a property object with just a getter:
>>> def getter(self): print('Get!')
...
>>> def setter(self, value): print('Set to {!r}!'.format(value))
...
>>> def deleter(self): print('Delete!')
...
>>> prop = property(getter)
>>> prop.fget is getter
True
>>> prop.fset is None
True
>>> prop.fdel is None
True
Next we use the .setter() method to add a setter:
>>> prop = prop.setter(setter)
>>> prop.fget is getter
True
>>> prop.fset is setter
True
>>> prop.fdel is None
True
Last we add a deleter with the .deleter() method:
>>> prop = prop.deleter(deleter)
>>> prop.fget is getter
True
>>> prop.fset is setter
True
>>> prop.fdel is deleter
True
Last but not least, the property object acts as a descriptor object, so it has .__get__(), .__set__() and .__delete__() methods to hook into instance attribute getting, setting and deleting:
>>> class Foo: pass
...
>>> prop.__get__(Foo(), Foo)
Get!
>>> prop.__set__(Foo(), 'bar')
Set to 'bar'!
>>> prop.__delete__(Foo())
Delete!
The Descriptor Howto includes a pure Python sample implementation of the property() type:
class Property:
"Emulate PyProperty_Type() in Objects/descrobject.c"
def __init__(self, fget=None, fset=None, fdel=None, doc=None):
self.fget = fget
self.fset = fset
self.fdel = fdel
if doc is None and fget is not None:
doc = fget.__doc__
self.__doc__ = doc
def __get__(self, obj, objtype=None):
if obj is None:
return self
if self.fget is None:
raise AttributeError("unreadable attribute")
return self.fget(obj)
def __set__(self, obj, value):
if self.fset is None:
raise AttributeError("can't set attribute")
self.fset(obj, value)
def __delete__(self, obj):
if self.fdel is None:
raise AttributeError("can't delete attribute")
self.fdel(obj)
def getter(self, fget):
return type(self)(fget, self.fset, self.fdel, self.__doc__)
def setter(self, fset):
return type(self)(self.fget, fset, self.fdel, self.__doc__)
def deleter(self, fdel):
return type(self)(self.fget, self.fset, fdel, self.__doc__)
The documentation says it's just a shortcut for creating read-only properties. So
#property
def x(self):
return self._x
is equivalent to
def getx(self):
return self._x
x = property(getx)
Here is a minimal example of how #property can be implemented:
class Thing:
def __init__(self, my_word):
self._word = my_word
#property
def word(self):
return self._word
>>> print( Thing('ok').word )
'ok'
Otherwise word remains a method instead of a property.
class Thing:
def __init__(self, my_word):
self._word = my_word
def word(self):
return self._word
>>> print( Thing('ok').word() )
'ok'
Below is another example on how #property can help when one has to refactor code which is taken from here (I only summarize it below):
Imagine you created a class Money like this:
class Money:
def __init__(self, dollars, cents):
self.dollars = dollars
self.cents = cents
and a user creates a library depending on this class where he/she uses e.g.
money = Money(27, 12)
print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 27 dollar and 12 cents.
Now let's suppose you decide to change your Money class and get rid of the dollars and cents attributes but instead decide to only track the total amount of cents:
class Money:
def __init__(self, dollars, cents):
self.total_cents = dollars * 100 + cents
If the above mentioned user now tries to run his/her library as before
money = Money(27, 12)
print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
it will result in an error
AttributeError: 'Money' object has no attribute 'dollars'
That means that now everyone who relies on your original Money class would have to change all lines of code where dollars and cents are used which can be very painful... So, how could this be avoided? By using #property!
That is how:
class Money:
def __init__(self, dollars, cents):
self.total_cents = dollars * 100 + cents
# Getter and setter for dollars...
#property
def dollars(self):
return self.total_cents // 100
#dollars.setter
def dollars(self, new_dollars):
self.total_cents = 100 * new_dollars + self.cents
# And the getter and setter for cents.
#property
def cents(self):
return self.total_cents % 100
#cents.setter
def cents(self, new_cents):
self.total_cents = 100 * self.dollars + new_cents
when we now call from our library
money = Money(27, 12)
print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 27 dollar and 12 cents.
it will work as expected and we did not have to change a single line of code in our library! In fact, we would not even have to know that the library we depend on changed.
Also the setter works fine:
money.dollars += 2
print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 29 dollar and 12 cents.
money.cents += 10
print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 29 dollar and 22 cents.
You can use #property also in abstract classes; I give a minimal example here.
The first part is simple:
#property
def x(self): ...
is the same as
def x(self): ...
x = property(x)
which, in turn, is the simplified syntax for creating a property with just a getter.
The next step would be to extend this property with a setter and a deleter. And this happens with the appropriate methods:
#x.setter
def x(self, value): ...
returns a new property which inherits everything from the old x plus the given setter.
x.deleter works the same way.
This following:
class C(object):
def __init__(self):
self._x = None
#property
def x(self):
"""I'm the 'x' property."""
return self._x
#x.setter
def x(self, value):
self._x = value
#x.deleter
def x(self):
del self._x
Is the same as:
class C(object):
def __init__(self):
self._x = None
def _x_get(self):
return self._x
def _x_set(self, value):
self._x = value
def _x_del(self):
del self._x
x = property(_x_get, _x_set, _x_del,
"I'm the 'x' property.")
Is the same as:
class C(object):
def __init__(self):
self._x = None
def _x_get(self):
return self._x
def _x_set(self, value):
self._x = value
def _x_del(self):
del self._x
x = property(_x_get, doc="I'm the 'x' property.")
x = x.setter(_x_set)
x = x.deleter(_x_del)
Is the same as:
class C(object):
def __init__(self):
self._x = None
def _x_get(self):
return self._x
x = property(_x_get, doc="I'm the 'x' property.")
def _x_set(self, value):
self._x = value
x = x.setter(_x_set)
def _x_del(self):
del self._x
x = x.deleter(_x_del)
Which is the same as :
class C(object):
def __init__(self):
self._x = None
#property
def x(self):
"""I'm the 'x' property."""
return self._x
#x.setter
def x(self, value):
self._x = value
#x.deleter
def x(self):
del self._x
Let's start with Python decorators.
A Python decorator is a function that helps to add some additional functionalities to an already defined function.
In Python, everything is an object. Functions in Python are first-class objects which means that they can be referenced by a variable, added in the lists, passed as arguments to another function, etc.
Consider the following code snippet.
def decorator_func(fun):
def wrapper_func():
print("Wrapper function started")
fun()
print("Given function decorated")
# Wrapper function add something to the passed function and decorator
# returns the wrapper function
return wrapper_func
def say_bye():
print("bye!!")
say_bye = decorator_func(say_bye)
say_bye()
# Output:
# Wrapper function started
# bye!!
# Given function decorated
Here, we can say that the decorator function modified our say_bye function and added some extra lines of code to it.
Python syntax for decorator
def decorator_func(fun):
def wrapper_func():
print("Wrapper function started")
fun()
print("Given function decorated")
# Wrapper function add something to the passed function and decorator
# returns the wrapper function
return wrapper_func
#decorator_func
def say_bye():
print("bye!!")
say_bye()
Let's go through everything with a case scenario. But before that, let's talk about some OOP principles.
Getters and setters are used in many object-oriented programming languages to ensure the principle of data encapsulation(which is seen as the bundling of data with the methods that operate on these data.)
These methods are, of course, the getter for retrieving the data and the setter for changing the data.
According to this principle, the attributes of a class are made private to hide and protect them from other code.
Yup, #property is basically a pythonic way to use getters and setters.
Python has a great concept called property which makes the life of an object-oriented programmer much simpler.
Let us assume that you decide to make a class that could store the temperature in degrees Celsius.
class Celsius:
def __init__(self, temperature = 0):
self.set_temperature(temperature)
def to_fahrenheit(self):
return (self.get_temperature() * 1.8) + 32
def get_temperature(self):
return self._temperature
def set_temperature(self, value):
if value < -273:
raise ValueError("Temperature below -273 is not possible")
self._temperature = value
Refactored Code, Here is how we could have achieved it with 'property.'
In Python, property() is a built-in function that creates and returns a property object.
A property object has three methods, getter(), setter(), and delete().
class Celsius:
def __init__(self, temperature = 0):
self.temperature = temperature
def to_fahrenheit(self):
return (self.temperature * 1.8) + 32
def get_temperature(self):
print("Getting value")
return self.temperature
def set_temperature(self, value):
if value < -273:
raise ValueError("Temperature below -273 is not possible")
print("Setting value")
self.temperature = value
temperature = property(get_temperature,set_temperature)
Here,
temperature = property(get_temperature,set_temperature)
could have been broken down as,
# make empty property
temperature = property()
# assign fget
temperature = temperature.getter(get_temperature)
# assign fset
temperature = temperature.setter(set_temperature)
Point To Note:
get_temperature remains a property instead of a method.
Now you can access the value of temperature by writing.
C = Celsius()
C.temperature
# instead of writing C.get_temperature()
We can go on further and not define names get_temperature and set_temperature as they are unnecessary and pollute the class namespace.
The pythonic way to deal with the above problem is to use #property.
class Celsius:
def __init__(self, temperature = 0):
self.temperature = temperature
def to_fahrenheit(self):
return (self.temperature * 1.8) + 32
#property
def temperature(self):
print("Getting value")
return self.temperature
#temperature.setter
def temperature(self, value):
if value < -273:
raise ValueError("Temperature below -273 is not possible")
print("Setting value")
self.temperature = value
Points to Note -
A method that is used for getting a value is decorated with "#property".
The method which has to function as the setter is decorated with "#temperature.setter", If the function had been called "x", we would have to decorate it with "#x.setter".
We wrote "two" methods with the same name and a different number of parameters, "def temperature(self)" and "def temperature(self,x)".
As you can see, the code is definitely less elegant.
Now, let's talk about one real-life practical scenario.
Let's say you have designed a class as follows:
class OurClass:
def __init__(self, a):
self.x = a
y = OurClass(10)
print(y.x)
Now, let's further assume that our class got popular among clients and they started using it in their programs, They did all kinds of assignments to the object.
And one fateful day, a trusted client came to us and suggested that "x" has to be a value between 0 and 1000; this is really a horrible scenario!
Due to properties, it's easy: We create a property version of "x".
class OurClass:
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
This is great, isn't it: You can start with the simplest implementation imaginable, and you are free to later migrate to a property version without having to change the interface! So properties are not just a replacement for getters and setters!
You can check this Implementation here
I read all the posts here and realized that we may need a real life example. Why, actually, we have #property?
So, consider a Flask app where you use authentication system.
You declare a model User in models.py:
class User(UserMixin, db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(64), unique=True, index=True)
username = db.Column(db.String(64), unique=True, index=True)
password_hash = db.Column(db.String(128))
...
#property
def password(self):
raise AttributeError('password is not a readable attribute')
#password.setter
def password(self, password):
self.password_hash = generate_password_hash(password)
def verify_password(self, password):
return check_password_hash(self.password_hash, password)
In this code we've "hidden" attribute password by using #property which triggers AttributeError assertion when you try to access it directly, while we used #property.setter to set the actual instance variable password_hash.
Now in auth/views.py we can instantiate a User with:
...
#auth.route('/register', methods=['GET', 'POST'])
def register():
form = RegisterForm()
if form.validate_on_submit():
user = User(email=form.email.data,
username=form.username.data,
password=form.password.data)
db.session.add(user)
db.session.commit()
...
Notice attribute password that comes from a registration form when a user fills the form. Password confirmation happens on the front end with EqualTo('password', message='Passwords must match') (in case if you are wondering, but it's a different topic related Flask forms).
I hope this example will be useful
This point is been cleared by many people up there but here is a direct point which I was searching.
This is what I feel is important to start with the #property decorator.
eg:-
class UtilityMixin():
#property
def get_config(self):
return "This is property"
The calling of function "get_config()" will work like this.
util = UtilityMixin()
print(util.get_config)
If you notice I have not used "()" brackets for calling the function. This is the basic thing which I was searching for the #property decorator. So that you can use your function just like a variable.
The best explanation can be found here:
Python #Property Explained – How to Use and When? (Full Examples)
by Selva Prabhakaran | Posted on November 5, 2018
It helped me understand WHY not only HOW.
https://www.machinelearningplus.com/python/python-property/
property is a class behind #property decorator.
You can always check this:
print(property) #<class 'property'>
I rewrote the example from help(property) to show that the #property syntax
class C:
def __init__(self):
self._x=None
#property
def x(self):
return self._x
#x.setter
def x(self, value):
self._x = value
#x.deleter
def x(self):
del self._x
c = C()
c.x="a"
print(c.x)
is functionally identical to property() syntax:
class C:
def __init__(self):
self._x=None
def g(self):
return self._x
def s(self, v):
self._x = v
def d(self):
del self._x
prop = property(g,s,d)
c = C()
c.x="a"
print(c.x)
There is no difference how we use the property as you can see.
To answer the question #property decorator is implemented via property class.
So, the question is to explain the property class a bit.
This line:
prop = property(g,s,d)
Was the initialization. We can rewrite it like this:
prop = property(fget=g,fset=s,fdel=d)
The meaning of fget, fset and fdel:
| fget
| function to be used for getting an attribute value
| fset
| function to be used for setting an attribute value
| fdel
| function to be used for del'ing an attribute
| doc
| docstring
The next image shows the triplets we have, from the class property:
__get__, __set__, and __delete__ are there to be overridden. This is the implementation of the descriptor pattern in Python.
In general, a descriptor is an object attribute with “binding behavior”, one whose attribute access has been overridden by methods in the descriptor protocol.
We can also use property setter, getter and deleter methods to bind the function to property. Check the next example. The method s2 of the class C will set the property doubled.
class C:
def __init__(self):
self._x=None
def g(self):
return self._x
def s(self, x):
self._x = x
def d(self):
del self._x
def s2(self,x):
self._x=x+x
x=property(g)
x=x.setter(s)
x=x.deleter(d)
c = C()
c.x="a"
print(c.x) # outputs "a"
C.x=property(C.g, C.s2)
C.x=C.x.deleter(C.d)
c2 = C()
c2.x="a"
print(c2.x) # outputs "aa"
A decorator is a function that takes a function as an argument and returns a closure. The closure is a set of inner functions and free variables. The inner function is closing over the free variable and that is why it is called 'closure'. A free variable is a variable that is outside the inner function and passed into the inner via docorator.
As the name says, decorator is decorating the received function.
function decorator(undecorated_func):
print("calling decorator func")
inner():
print("I am inside inner")
return undecorated_func
return inner
this is a simple decorator function. It received "undecorated_func" and passed it to inner() as a free variable, inner() printed "I am inside inner" and returned undecorated_func. When we call decorator(undecorated_func), it is returning the inner. Here is the key, in decorators we are naming the inner function as the name of the function that we passed.
undecorated_function= decorator(undecorated_func)
now inner function is called "undecorated_func". Since inner is now named as "undecorated_func", we passed "undecorated_func" to the decorator and we returned "undecorated_func" plus printed out "I am inside inner". so this print statement decorated our "undecorated_func".
now let's define a class with a property decorator:
class Person:
def __init__(self,name):
self._name=name
#property
def name(self):
return self._name
#name.setter
def name(self.value):
self._name=value
when we decorated name() with #property(), this is what happened:
name=property(name) # Person.__dict__ you ll see name
first argument of property() is getter. this is what happened in the second decoration:
name=name.setter(name)
As I mentioned above, the decorator returns the inner function, and we name the inner function with the name of the function that we passed.
Here is an important thing to be aware of. "name" is immutable. in the first decoration we got this:
name=property(name)
in the second one we got this
name=name.setter(name)
We are not modifying name obj. In the second decoration, python sees that this is property object and it already had getter. So python creates a new "name" object, adds the "fget" from the first obj and then sets the "fset".
A property can be declared in two ways.
Creating the getter, setter methods for an attribute and then passing these as argument to property function
Using the #property decorator.
You can have a look at few examples I have written about properties in python.
In the following, I have given an example to clarify #property
Consider a class named Student with two variables: name and class_number and you want class_number to be in the range of 1 to 5.
Now I will explain two wrong solutions and finally the correct one:
The code below is wrong because it doesn't validate the class_number (to be in the range 1 to 5)
class Student:
def __init__(self, name, class_number):
self.name = name
self.class_number = class_number
Despite validation, this solution is also wrong:
def validate_class_number(number):
if 1 <= number <= 5:
return number
else:
raise Exception("class number should be in the range of 1 to 5")
class Student:
def __init__(self, name, class_number):
self.name = name
self.class_number = validate_class_number(class_number)
Because class_number validation is checked only at the time of making a class instance and it is not checked after that (it is possible to change class_number with a number outside of the range 1 to 5):
student1 = Student("masoud",5)
student1.class_number = 7
The correct solution is:
class Student:
def __init__(self, name, class_number):
self.name = name
self.class_number = class_number
#property
def class_number(self):
return self._class_number
#class_number.setter
def class_number(self, class_number):
if not (1 <= class_number <= 5): raise Exception("class number should be in the range of 1 to 5")
self._class_number = class_number
Here is another example:
##
## Python Properties Example
##
class GetterSetterExample( object ):
## Set the default value for x ( we reference it using self.x, set a value using self.x = value )
__x = None
##
## On Class Initialization - do something... if we want..
##
def __init__( self ):
## Set a value to __x through the getter / setter... Since __x is defined above, this doesn't need to be set...
self.x = 1234
return None
##
## Define x as a property, ie a getter - All getters should have a default value arg, so I added it - it will not be passed in when setting a value, so you need to set the default here so it will be used..
##
#property
def x( self, _default = None ):
## I added an optional default value argument as all getters should have this - set it to the default value you want to return...
_value = ( self.__x, _default )[ self.__x == None ]
## Debugging - so you can see the order the calls are made...
print( '[ Test Class ] Get x = ' + str( _value ) )
## Return the value - we are a getter afterall...
return _value
##
## Define the setter function for x...
##
#x.setter
def x( self, _value = None ):
## Debugging - so you can see the order the calls are made...
print( '[ Test Class ] Set x = ' + str( _value ) )
## This is to show the setter function works.... If the value is above 0, set it to a negative value... otherwise keep it as is ( 0 is the only non-negative number, it can't be negative or positive anyway )
if ( _value > 0 ):
self.__x = -_value
else:
self.__x = _value
##
## Define the deleter function for x...
##
#x.deleter
def x( self ):
## Unload the assignment / data for x
if ( self.__x != None ):
del self.__x
##
## To String / Output Function for the class - this will show the property value for each property we add...
##
def __str__( self ):
## Output the x property data...
print( '[ x ] ' + str( self.x ) )
## Return a new line - technically we should return a string so it can be printed where we want it, instead of printed early if _data = str( C( ) ) is used....
return '\n'
##
##
##
_test = GetterSetterExample( )
print( _test )
## For some reason the deleter isn't being called...
del _test.x
Basically, the same as the C( object ) example except I'm using x instead... I also don't initialize in __init - ... well.. I do, but it can be removed because __x is defined as part of the class....
The output is:
[ Test Class ] Set x = 1234
[ Test Class ] Get x = -1234
[ x ] -1234
and if I comment out the self.x = 1234 in init then the output is:
[ Test Class ] Get x = None
[ x ] None
and if I set the _default = None to _default = 0 in the getter function ( as all getters should have a default value but it isn't passed in by the property values from what I've seen so you can define it here, and it actually isn't bad because you can define the default once and use it everywhere ) ie: def x( self, _default = 0 ):
[ Test Class ] Get x = 0
[ x ] 0
Note: The getter logic is there just to have the value be manipulated by it to ensure it is manipulated by it - the same for the print statements...
Note: I'm used to Lua and being able to dynamically create 10+ helpers when I call a single function and I made something similar for Python without using properties and it works to a degree, but, even though the functions are being created before being used, there are still issues at times with them being called prior to being created which is strange as it isn't coded that way... I prefer the flexibility of Lua meta-tables and the fact I can use actual setters / getters instead of essentially directly accessing a variable... I do like how quickly some things can be built with Python though - for instance gui programs. although one I am designing may not be possible without a lot of additional libraries - if I code it in AutoHotkey I can directly access the dll calls I need, and the same can be done in Java, C#, C++, and more - maybe I haven't found the right thing yet but for that project I may switch from Python..
Note: The code output in this forum is broken - I had to add spaces to the first part of the code for it to work - when copy / pasting ensure you convert all spaces to tabs.... I use tabs for Python because in a file which is 10,000 lines the filesize can be 512KB to 1MB with spaces and 100 to 200KB with tabs which equates to a massive difference for file size, and reduction in processing time...
Tabs can also be adjusted per user - so if you prefer 2 spaces width, 4, 8 or whatever you can do it meaning it is thoughtful for developers with eye-sight deficits.
Note: All of the functions defined in the class aren't indented properly because of a bug in the forum software - ensure you indent it if you copy / paste
I have a class in which a method first needs to verify that an attribute is present and otherwise call a function to compute it. Then, ensuring that the attribute is not None, it performs some operations with it. I can see two slightly different design choices:
class myclass():
def __init__(self):
self.attr = None
def compute_attribute(self):
self.attr = 1
def print_attribute(self):
if self.attr is None:
self.compute_attribute()
print self.attr
And
class myclass2():
def __init__(self):
pass
def compute_attribute(self):
self.attr = 1
return self.attr
def print_attribute(self):
try:
attr = self.attr
except AttributeError:
attr = self.compute_attribute()
if attr is not None:
print attr
In the first design, I need to make sure that all the class attributes are set to None in advance, which can become verbose but also clarify the structure of the object.
The second choice seems to be the more widely used one. However, for my purposes (scientific computing related to information theory) using try except blocks everywhere can be a bit of an overkill given that this class doesn't really interact with other classes, it just takes data and computes a bunch of things.
Firstly, you can use hasattr to check if an object has an attribute, it returns True if the attribute exists.
hasattr(object, attribute) # will return True if the object has the attribute
Secondly, You can customise attribute access in Python, you can read more about it here: https://docs.python.org/2/reference/datamodel.html#customizing-attribute-access
Basically, you override the __getattr__ method to achieve this, so something like:
class myclass2():
def init(self):
pass
def compute_attr(self):
self.attr = 1
return self.attr
def print_attribute(self):
print self.attr
def __getattr__(self, name):
if hasattr(self, name) and getattr(self, name)!=None:
return getattr(self, name):
else:
compute_method="compute_"+name;
if hasattr(self, compute_method):
return getattr(self, compute_method)()
Make sure you only use getattr to access the attribute within __getattr__ or you'll end up with infinite recursion
Based on the answer jonrsharpe linked, I offer a third design choice. The idea here is that no special conditional logic is required at all either by the clients of MyClass or by code within MyClass itself. Instead, a decorator is applied to a function that does the (hypothetically expensive) computation of the property, and then that result is stored.
This means that the expensive computation is done lazily (only if a client tries to access the property) and only performed once.
def lazyprop(fn):
attr_name = '_lazy_' + fn.__name__
#property
def _lazyprop(self):
if not hasattr(self, attr_name):
setattr(self, attr_name, fn(self))
return getattr(self, attr_name)
return _lazyprop
class MyClass(object):
#lazyprop
def attr(self):
print('Generating attr')
return 1
def __repr__(self):
return str(self.attr)
if __name__ == '__main__':
o = MyClass()
print(o.__dict__, end='\n\n')
print(o, end='\n\n')
print(o.__dict__, end='\n\n')
print(o)
Output
{}
Generating attr
1
{'_lazy_attr': 1}
1
Edit
Application of Cyclone's answer to OP's context:
class lazy_property(object):
'''
meant to be used for lazy evaluation of an object attribute.
property should represent non-mutable data, as it replaces itself.
'''
def __init__(self, fget):
self.fget = fget
self.func_name = fget.__name__
def __get__(self, obj, cls):
if obj is None:
return None
value = self.fget(obj)
setattr(obj, self.func_name, value)
return value
class MyClass(object):
#lazy_property
def attr(self):
print('Generating attr')
return 1
def __repr__(self):
return str(self.attr)
if __name__ == '__main__':
o = MyClass()
print(o.__dict__, end='\n\n')
print(o, end='\n\n')
print(o.__dict__, end='\n\n')
print(o)
The output is identical to above.
I would like to understand how the built-in function property works. What confuses me is that property can also be used as a decorator, but it only takes arguments when used as a built-in function and not when used as a decorator.
This example is from the documentation:
class C:
def __init__(self):
self._x = None
def getx(self):
return self._x
def setx(self, value):
self._x = value
def delx(self):
del self._x
x = property(getx, setx, delx, "I'm the 'x' property.")
property's arguments are getx, setx, delx and a doc string.
In the code below property is used as a decorator. The object of it is the x function, but in the code above there is no place for an object function in the arguments.
class C:
def __init__(self):
self._x = None
#property
def x(self):
"""I'm the 'x' property."""
return self._x
#x.setter
def x(self, value):
self._x = value
#x.deleter
def x(self):
del self._x
How are the x.setter and x.deleter decorators created in this case?
The property() function returns a special descriptor object:
>>> property()
<property object at 0x10ff07940>
It is this object that has extra methods:
>>> property().getter
<built-in method getter of property object at 0x10ff07998>
>>> property().setter
<built-in method setter of property object at 0x10ff07940>
>>> property().deleter
<built-in method deleter of property object at 0x10ff07998>
These act as decorators too. They return a new property object:
>>> property().getter(None)
<property object at 0x10ff079f0>
that is a copy of the old object, but with one of the functions replaced.
Remember, that the #decorator syntax is just syntactic sugar; the syntax:
#property
def foo(self): return self._foo
really means the same thing as
def foo(self): return self._foo
foo = property(foo)
so foo the function is replaced by property(foo), which we saw above is a special object. Then when you use #foo.setter(), what you are doing is call that property().setter method I showed you above, which returns a new copy of the property, but this time with the setter function replaced with the decorated method.
The following sequence also creates a full-on property, by using those decorator methods.
First we create some functions and a property object with just a getter:
>>> def getter(self): print('Get!')
...
>>> def setter(self, value): print('Set to {!r}!'.format(value))
...
>>> def deleter(self): print('Delete!')
...
>>> prop = property(getter)
>>> prop.fget is getter
True
>>> prop.fset is None
True
>>> prop.fdel is None
True
Next we use the .setter() method to add a setter:
>>> prop = prop.setter(setter)
>>> prop.fget is getter
True
>>> prop.fset is setter
True
>>> prop.fdel is None
True
Last we add a deleter with the .deleter() method:
>>> prop = prop.deleter(deleter)
>>> prop.fget is getter
True
>>> prop.fset is setter
True
>>> prop.fdel is deleter
True
Last but not least, the property object acts as a descriptor object, so it has .__get__(), .__set__() and .__delete__() methods to hook into instance attribute getting, setting and deleting:
>>> class Foo: pass
...
>>> prop.__get__(Foo(), Foo)
Get!
>>> prop.__set__(Foo(), 'bar')
Set to 'bar'!
>>> prop.__delete__(Foo())
Delete!
The Descriptor Howto includes a pure Python sample implementation of the property() type:
class Property:
"Emulate PyProperty_Type() in Objects/descrobject.c"
def __init__(self, fget=None, fset=None, fdel=None, doc=None):
self.fget = fget
self.fset = fset
self.fdel = fdel
if doc is None and fget is not None:
doc = fget.__doc__
self.__doc__ = doc
def __get__(self, obj, objtype=None):
if obj is None:
return self
if self.fget is None:
raise AttributeError("unreadable attribute")
return self.fget(obj)
def __set__(self, obj, value):
if self.fset is None:
raise AttributeError("can't set attribute")
self.fset(obj, value)
def __delete__(self, obj):
if self.fdel is None:
raise AttributeError("can't delete attribute")
self.fdel(obj)
def getter(self, fget):
return type(self)(fget, self.fset, self.fdel, self.__doc__)
def setter(self, fset):
return type(self)(self.fget, fset, self.fdel, self.__doc__)
def deleter(self, fdel):
return type(self)(self.fget, self.fset, fdel, self.__doc__)
The documentation says it's just a shortcut for creating read-only properties. So
#property
def x(self):
return self._x
is equivalent to
def getx(self):
return self._x
x = property(getx)
Here is a minimal example of how #property can be implemented:
class Thing:
def __init__(self, my_word):
self._word = my_word
#property
def word(self):
return self._word
>>> print( Thing('ok').word )
'ok'
Otherwise word remains a method instead of a property.
class Thing:
def __init__(self, my_word):
self._word = my_word
def word(self):
return self._word
>>> print( Thing('ok').word() )
'ok'
Below is another example on how #property can help when one has to refactor code which is taken from here (I only summarize it below):
Imagine you created a class Money like this:
class Money:
def __init__(self, dollars, cents):
self.dollars = dollars
self.cents = cents
and a user creates a library depending on this class where he/she uses e.g.
money = Money(27, 12)
print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 27 dollar and 12 cents.
Now let's suppose you decide to change your Money class and get rid of the dollars and cents attributes but instead decide to only track the total amount of cents:
class Money:
def __init__(self, dollars, cents):
self.total_cents = dollars * 100 + cents
If the above mentioned user now tries to run his/her library as before
money = Money(27, 12)
print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
it will result in an error
AttributeError: 'Money' object has no attribute 'dollars'
That means that now everyone who relies on your original Money class would have to change all lines of code where dollars and cents are used which can be very painful... So, how could this be avoided? By using #property!
That is how:
class Money:
def __init__(self, dollars, cents):
self.total_cents = dollars * 100 + cents
# Getter and setter for dollars...
#property
def dollars(self):
return self.total_cents // 100
#dollars.setter
def dollars(self, new_dollars):
self.total_cents = 100 * new_dollars + self.cents
# And the getter and setter for cents.
#property
def cents(self):
return self.total_cents % 100
#cents.setter
def cents(self, new_cents):
self.total_cents = 100 * self.dollars + new_cents
when we now call from our library
money = Money(27, 12)
print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 27 dollar and 12 cents.
it will work as expected and we did not have to change a single line of code in our library! In fact, we would not even have to know that the library we depend on changed.
Also the setter works fine:
money.dollars += 2
print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 29 dollar and 12 cents.
money.cents += 10
print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 29 dollar and 22 cents.
You can use #property also in abstract classes; I give a minimal example here.
The first part is simple:
#property
def x(self): ...
is the same as
def x(self): ...
x = property(x)
which, in turn, is the simplified syntax for creating a property with just a getter.
The next step would be to extend this property with a setter and a deleter. And this happens with the appropriate methods:
#x.setter
def x(self, value): ...
returns a new property which inherits everything from the old x plus the given setter.
x.deleter works the same way.
This following:
class C(object):
def __init__(self):
self._x = None
#property
def x(self):
"""I'm the 'x' property."""
return self._x
#x.setter
def x(self, value):
self._x = value
#x.deleter
def x(self):
del self._x
Is the same as:
class C(object):
def __init__(self):
self._x = None
def _x_get(self):
return self._x
def _x_set(self, value):
self._x = value
def _x_del(self):
del self._x
x = property(_x_get, _x_set, _x_del,
"I'm the 'x' property.")
Is the same as:
class C(object):
def __init__(self):
self._x = None
def _x_get(self):
return self._x
def _x_set(self, value):
self._x = value
def _x_del(self):
del self._x
x = property(_x_get, doc="I'm the 'x' property.")
x = x.setter(_x_set)
x = x.deleter(_x_del)
Is the same as:
class C(object):
def __init__(self):
self._x = None
def _x_get(self):
return self._x
x = property(_x_get, doc="I'm the 'x' property.")
def _x_set(self, value):
self._x = value
x = x.setter(_x_set)
def _x_del(self):
del self._x
x = x.deleter(_x_del)
Which is the same as :
class C(object):
def __init__(self):
self._x = None
#property
def x(self):
"""I'm the 'x' property."""
return self._x
#x.setter
def x(self, value):
self._x = value
#x.deleter
def x(self):
del self._x
Let's start with Python decorators.
A Python decorator is a function that helps to add some additional functionalities to an already defined function.
In Python, everything is an object. Functions in Python are first-class objects which means that they can be referenced by a variable, added in the lists, passed as arguments to another function, etc.
Consider the following code snippet.
def decorator_func(fun):
def wrapper_func():
print("Wrapper function started")
fun()
print("Given function decorated")
# Wrapper function add something to the passed function and decorator
# returns the wrapper function
return wrapper_func
def say_bye():
print("bye!!")
say_bye = decorator_func(say_bye)
say_bye()
# Output:
# Wrapper function started
# bye!!
# Given function decorated
Here, we can say that the decorator function modified our say_bye function and added some extra lines of code to it.
Python syntax for decorator
def decorator_func(fun):
def wrapper_func():
print("Wrapper function started")
fun()
print("Given function decorated")
# Wrapper function add something to the passed function and decorator
# returns the wrapper function
return wrapper_func
#decorator_func
def say_bye():
print("bye!!")
say_bye()
Let's go through everything with a case scenario. But before that, let's talk about some OOP principles.
Getters and setters are used in many object-oriented programming languages to ensure the principle of data encapsulation(which is seen as the bundling of data with the methods that operate on these data.)
These methods are, of course, the getter for retrieving the data and the setter for changing the data.
According to this principle, the attributes of a class are made private to hide and protect them from other code.
Yup, #property is basically a pythonic way to use getters and setters.
Python has a great concept called property which makes the life of an object-oriented programmer much simpler.
Let us assume that you decide to make a class that could store the temperature in degrees Celsius.
class Celsius:
def __init__(self, temperature = 0):
self.set_temperature(temperature)
def to_fahrenheit(self):
return (self.get_temperature() * 1.8) + 32
def get_temperature(self):
return self._temperature
def set_temperature(self, value):
if value < -273:
raise ValueError("Temperature below -273 is not possible")
self._temperature = value
Refactored Code, Here is how we could have achieved it with 'property.'
In Python, property() is a built-in function that creates and returns a property object.
A property object has three methods, getter(), setter(), and delete().
class Celsius:
def __init__(self, temperature = 0):
self.temperature = temperature
def to_fahrenheit(self):
return (self.temperature * 1.8) + 32
def get_temperature(self):
print("Getting value")
return self.temperature
def set_temperature(self, value):
if value < -273:
raise ValueError("Temperature below -273 is not possible")
print("Setting value")
self.temperature = value
temperature = property(get_temperature,set_temperature)
Here,
temperature = property(get_temperature,set_temperature)
could have been broken down as,
# make empty property
temperature = property()
# assign fget
temperature = temperature.getter(get_temperature)
# assign fset
temperature = temperature.setter(set_temperature)
Point To Note:
get_temperature remains a property instead of a method.
Now you can access the value of temperature by writing.
C = Celsius()
C.temperature
# instead of writing C.get_temperature()
We can go on further and not define names get_temperature and set_temperature as they are unnecessary and pollute the class namespace.
The pythonic way to deal with the above problem is to use #property.
class Celsius:
def __init__(self, temperature = 0):
self.temperature = temperature
def to_fahrenheit(self):
return (self.temperature * 1.8) + 32
#property
def temperature(self):
print("Getting value")
return self.temperature
#temperature.setter
def temperature(self, value):
if value < -273:
raise ValueError("Temperature below -273 is not possible")
print("Setting value")
self.temperature = value
Points to Note -
A method that is used for getting a value is decorated with "#property".
The method which has to function as the setter is decorated with "#temperature.setter", If the function had been called "x", we would have to decorate it with "#x.setter".
We wrote "two" methods with the same name and a different number of parameters, "def temperature(self)" and "def temperature(self,x)".
As you can see, the code is definitely less elegant.
Now, let's talk about one real-life practical scenario.
Let's say you have designed a class as follows:
class OurClass:
def __init__(self, a):
self.x = a
y = OurClass(10)
print(y.x)
Now, let's further assume that our class got popular among clients and they started using it in their programs, They did all kinds of assignments to the object.
And one fateful day, a trusted client came to us and suggested that "x" has to be a value between 0 and 1000; this is really a horrible scenario!
Due to properties, it's easy: We create a property version of "x".
class OurClass:
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
This is great, isn't it: You can start with the simplest implementation imaginable, and you are free to later migrate to a property version without having to change the interface! So properties are not just a replacement for getters and setters!
You can check this Implementation here
I read all the posts here and realized that we may need a real life example. Why, actually, we have #property?
So, consider a Flask app where you use authentication system.
You declare a model User in models.py:
class User(UserMixin, db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(64), unique=True, index=True)
username = db.Column(db.String(64), unique=True, index=True)
password_hash = db.Column(db.String(128))
...
#property
def password(self):
raise AttributeError('password is not a readable attribute')
#password.setter
def password(self, password):
self.password_hash = generate_password_hash(password)
def verify_password(self, password):
return check_password_hash(self.password_hash, password)
In this code we've "hidden" attribute password by using #property which triggers AttributeError assertion when you try to access it directly, while we used #property.setter to set the actual instance variable password_hash.
Now in auth/views.py we can instantiate a User with:
...
#auth.route('/register', methods=['GET', 'POST'])
def register():
form = RegisterForm()
if form.validate_on_submit():
user = User(email=form.email.data,
username=form.username.data,
password=form.password.data)
db.session.add(user)
db.session.commit()
...
Notice attribute password that comes from a registration form when a user fills the form. Password confirmation happens on the front end with EqualTo('password', message='Passwords must match') (in case if you are wondering, but it's a different topic related Flask forms).
I hope this example will be useful
This point is been cleared by many people up there but here is a direct point which I was searching.
This is what I feel is important to start with the #property decorator.
eg:-
class UtilityMixin():
#property
def get_config(self):
return "This is property"
The calling of function "get_config()" will work like this.
util = UtilityMixin()
print(util.get_config)
If you notice I have not used "()" brackets for calling the function. This is the basic thing which I was searching for the #property decorator. So that you can use your function just like a variable.
The best explanation can be found here:
Python #Property Explained – How to Use and When? (Full Examples)
by Selva Prabhakaran | Posted on November 5, 2018
It helped me understand WHY not only HOW.
https://www.machinelearningplus.com/python/python-property/
property is a class behind #property decorator.
You can always check this:
print(property) #<class 'property'>
I rewrote the example from help(property) to show that the #property syntax
class C:
def __init__(self):
self._x=None
#property
def x(self):
return self._x
#x.setter
def x(self, value):
self._x = value
#x.deleter
def x(self):
del self._x
c = C()
c.x="a"
print(c.x)
is functionally identical to property() syntax:
class C:
def __init__(self):
self._x=None
def g(self):
return self._x
def s(self, v):
self._x = v
def d(self):
del self._x
prop = property(g,s,d)
c = C()
c.x="a"
print(c.x)
There is no difference how we use the property as you can see.
To answer the question #property decorator is implemented via property class.
So, the question is to explain the property class a bit.
This line:
prop = property(g,s,d)
Was the initialization. We can rewrite it like this:
prop = property(fget=g,fset=s,fdel=d)
The meaning of fget, fset and fdel:
| fget
| function to be used for getting an attribute value
| fset
| function to be used for setting an attribute value
| fdel
| function to be used for del'ing an attribute
| doc
| docstring
The next image shows the triplets we have, from the class property:
__get__, __set__, and __delete__ are there to be overridden. This is the implementation of the descriptor pattern in Python.
In general, a descriptor is an object attribute with “binding behavior”, one whose attribute access has been overridden by methods in the descriptor protocol.
We can also use property setter, getter and deleter methods to bind the function to property. Check the next example. The method s2 of the class C will set the property doubled.
class C:
def __init__(self):
self._x=None
def g(self):
return self._x
def s(self, x):
self._x = x
def d(self):
del self._x
def s2(self,x):
self._x=x+x
x=property(g)
x=x.setter(s)
x=x.deleter(d)
c = C()
c.x="a"
print(c.x) # outputs "a"
C.x=property(C.g, C.s2)
C.x=C.x.deleter(C.d)
c2 = C()
c2.x="a"
print(c2.x) # outputs "aa"
A decorator is a function that takes a function as an argument and returns a closure. The closure is a set of inner functions and free variables. The inner function is closing over the free variable and that is why it is called 'closure'. A free variable is a variable that is outside the inner function and passed into the inner via docorator.
As the name says, decorator is decorating the received function.
function decorator(undecorated_func):
print("calling decorator func")
inner():
print("I am inside inner")
return undecorated_func
return inner
this is a simple decorator function. It received "undecorated_func" and passed it to inner() as a free variable, inner() printed "I am inside inner" and returned undecorated_func. When we call decorator(undecorated_func), it is returning the inner. Here is the key, in decorators we are naming the inner function as the name of the function that we passed.
undecorated_function= decorator(undecorated_func)
now inner function is called "undecorated_func". Since inner is now named as "undecorated_func", we passed "undecorated_func" to the decorator and we returned "undecorated_func" plus printed out "I am inside inner". so this print statement decorated our "undecorated_func".
now let's define a class with a property decorator:
class Person:
def __init__(self,name):
self._name=name
#property
def name(self):
return self._name
#name.setter
def name(self.value):
self._name=value
when we decorated name() with #property(), this is what happened:
name=property(name) # Person.__dict__ you ll see name
first argument of property() is getter. this is what happened in the second decoration:
name=name.setter(name)
As I mentioned above, the decorator returns the inner function, and we name the inner function with the name of the function that we passed.
Here is an important thing to be aware of. "name" is immutable. in the first decoration we got this:
name=property(name)
in the second one we got this
name=name.setter(name)
We are not modifying name obj. In the second decoration, python sees that this is property object and it already had getter. So python creates a new "name" object, adds the "fget" from the first obj and then sets the "fset".
A property can be declared in two ways.
Creating the getter, setter methods for an attribute and then passing these as argument to property function
Using the #property decorator.
You can have a look at few examples I have written about properties in python.
In the following, I have given an example to clarify #property
Consider a class named Student with two variables: name and class_number and you want class_number to be in the range of 1 to 5.
Now I will explain two wrong solutions and finally the correct one:
The code below is wrong because it doesn't validate the class_number (to be in the range 1 to 5)
class Student:
def __init__(self, name, class_number):
self.name = name
self.class_number = class_number
Despite validation, this solution is also wrong:
def validate_class_number(number):
if 1 <= number <= 5:
return number
else:
raise Exception("class number should be in the range of 1 to 5")
class Student:
def __init__(self, name, class_number):
self.name = name
self.class_number = validate_class_number(class_number)
Because class_number validation is checked only at the time of making a class instance and it is not checked after that (it is possible to change class_number with a number outside of the range 1 to 5):
student1 = Student("masoud",5)
student1.class_number = 7
The correct solution is:
class Student:
def __init__(self, name, class_number):
self.name = name
self.class_number = class_number
#property
def class_number(self):
return self._class_number
#class_number.setter
def class_number(self, class_number):
if not (1 <= class_number <= 5): raise Exception("class number should be in the range of 1 to 5")
self._class_number = class_number
Here is another example:
##
## Python Properties Example
##
class GetterSetterExample( object ):
## Set the default value for x ( we reference it using self.x, set a value using self.x = value )
__x = None
##
## On Class Initialization - do something... if we want..
##
def __init__( self ):
## Set a value to __x through the getter / setter... Since __x is defined above, this doesn't need to be set...
self.x = 1234
return None
##
## Define x as a property, ie a getter - All getters should have a default value arg, so I added it - it will not be passed in when setting a value, so you need to set the default here so it will be used..
##
#property
def x( self, _default = None ):
## I added an optional default value argument as all getters should have this - set it to the default value you want to return...
_value = ( self.__x, _default )[ self.__x == None ]
## Debugging - so you can see the order the calls are made...
print( '[ Test Class ] Get x = ' + str( _value ) )
## Return the value - we are a getter afterall...
return _value
##
## Define the setter function for x...
##
#x.setter
def x( self, _value = None ):
## Debugging - so you can see the order the calls are made...
print( '[ Test Class ] Set x = ' + str( _value ) )
## This is to show the setter function works.... If the value is above 0, set it to a negative value... otherwise keep it as is ( 0 is the only non-negative number, it can't be negative or positive anyway )
if ( _value > 0 ):
self.__x = -_value
else:
self.__x = _value
##
## Define the deleter function for x...
##
#x.deleter
def x( self ):
## Unload the assignment / data for x
if ( self.__x != None ):
del self.__x
##
## To String / Output Function for the class - this will show the property value for each property we add...
##
def __str__( self ):
## Output the x property data...
print( '[ x ] ' + str( self.x ) )
## Return a new line - technically we should return a string so it can be printed where we want it, instead of printed early if _data = str( C( ) ) is used....
return '\n'
##
##
##
_test = GetterSetterExample( )
print( _test )
## For some reason the deleter isn't being called...
del _test.x
Basically, the same as the C( object ) example except I'm using x instead... I also don't initialize in __init - ... well.. I do, but it can be removed because __x is defined as part of the class....
The output is:
[ Test Class ] Set x = 1234
[ Test Class ] Get x = -1234
[ x ] -1234
and if I comment out the self.x = 1234 in init then the output is:
[ Test Class ] Get x = None
[ x ] None
and if I set the _default = None to _default = 0 in the getter function ( as all getters should have a default value but it isn't passed in by the property values from what I've seen so you can define it here, and it actually isn't bad because you can define the default once and use it everywhere ) ie: def x( self, _default = 0 ):
[ Test Class ] Get x = 0
[ x ] 0
Note: The getter logic is there just to have the value be manipulated by it to ensure it is manipulated by it - the same for the print statements...
Note: I'm used to Lua and being able to dynamically create 10+ helpers when I call a single function and I made something similar for Python without using properties and it works to a degree, but, even though the functions are being created before being used, there are still issues at times with them being called prior to being created which is strange as it isn't coded that way... I prefer the flexibility of Lua meta-tables and the fact I can use actual setters / getters instead of essentially directly accessing a variable... I do like how quickly some things can be built with Python though - for instance gui programs. although one I am designing may not be possible without a lot of additional libraries - if I code it in AutoHotkey I can directly access the dll calls I need, and the same can be done in Java, C#, C++, and more - maybe I haven't found the right thing yet but for that project I may switch from Python..
Note: The code output in this forum is broken - I had to add spaces to the first part of the code for it to work - when copy / pasting ensure you convert all spaces to tabs.... I use tabs for Python because in a file which is 10,000 lines the filesize can be 512KB to 1MB with spaces and 100 to 200KB with tabs which equates to a massive difference for file size, and reduction in processing time...
Tabs can also be adjusted per user - so if you prefer 2 spaces width, 4, 8 or whatever you can do it meaning it is thoughtful for developers with eye-sight deficits.
Note: All of the functions defined in the class aren't indented properly because of a bug in the forum software - ensure you indent it if you copy / paste
This question already has answers here:
Using property() on classmethods
(19 answers)
Closed 3 years ago.
In python I can add a method to a class with the #classmethod decorator. Is there a similar decorator to add a property to a class? I can better show what I'm talking about.
class Example(object):
the_I = 10
def __init__( self ):
self.an_i = 20
#property
def i( self ):
return self.an_i
def inc_i( self ):
self.an_i += 1
# is this even possible?
#classproperty
def I( cls ):
return cls.the_I
#classmethod
def inc_I( cls ):
cls.the_I += 1
e = Example()
assert e.i == 20
e.inc_i()
assert e.i == 21
assert Example.I == 10
Example.inc_I()
assert Example.I == 11
Is the syntax I've used above possible or would it require something more?
The reason I want class properties is so I can lazy load class attributes, which seems reasonable enough.
Here's how I would do this:
class ClassPropertyDescriptor(object):
def __init__(self, fget, fset=None):
self.fget = fget
self.fset = fset
def __get__(self, obj, klass=None):
if klass is None:
klass = type(obj)
return self.fget.__get__(obj, klass)()
def __set__(self, obj, value):
if not self.fset:
raise AttributeError("can't set attribute")
type_ = type(obj)
return self.fset.__get__(obj, type_)(value)
def setter(self, func):
if not isinstance(func, (classmethod, staticmethod)):
func = classmethod(func)
self.fset = func
return self
def classproperty(func):
if not isinstance(func, (classmethod, staticmethod)):
func = classmethod(func)
return ClassPropertyDescriptor(func)
class Bar(object):
_bar = 1
#classproperty
def bar(cls):
return cls._bar
#bar.setter
def bar(cls, value):
cls._bar = value
# test instance instantiation
foo = Bar()
assert foo.bar == 1
baz = Bar()
assert baz.bar == 1
# test static variable
baz.bar = 5
assert foo.bar == 5
# test setting variable on the class
Bar.bar = 50
assert baz.bar == 50
assert foo.bar == 50
The setter didn't work at the time we call Bar.bar, because we are calling
TypeOfBar.bar.__set__, which is not Bar.bar.__set__.
Adding a metaclass definition solves this:
class ClassPropertyMetaClass(type):
def __setattr__(self, key, value):
if key in self.__dict__:
obj = self.__dict__.get(key)
if obj and type(obj) is ClassPropertyDescriptor:
return obj.__set__(self, value)
return super(ClassPropertyMetaClass, self).__setattr__(key, value)
# and update class define:
# class Bar(object):
# __metaclass__ = ClassPropertyMetaClass
# _bar = 1
# and update ClassPropertyDescriptor.__set__
# def __set__(self, obj, value):
# if not self.fset:
# raise AttributeError("can't set attribute")
# if inspect.isclass(obj):
# type_ = obj
# obj = None
# else:
# type_ = type(obj)
# return self.fset.__get__(obj, type_)(value)
Now all will be fine.
If you define classproperty as follows, then your example works exactly as you requested.
class classproperty(object):
def __init__(self, f):
self.f = f
def __get__(self, obj, owner):
return self.f(owner)
The caveat is that you can't use this for writable properties. While e.I = 20 will raise an AttributeError, Example.I = 20 will overwrite the property object itself.
[answer written based on python 3.4; the metaclass syntax differs in 2 but I think the technique will still work]
You can do this with a metaclass...mostly. Dappawit's almost works, but I think it has a flaw:
class MetaFoo(type):
#property
def thingy(cls):
return cls._thingy
class Foo(object, metaclass=MetaFoo):
_thingy = 23
This gets you a classproperty on Foo, but there's a problem...
print("Foo.thingy is {}".format(Foo.thingy))
# Foo.thingy is 23
# Yay, the classmethod-property is working as intended!
foo = Foo()
if hasattr(foo, "thingy"):
print("Foo().thingy is {}".format(foo.thingy))
else:
print("Foo instance has no attribute 'thingy'")
# Foo instance has no attribute 'thingy'
# Wha....?
What the hell is going on here? Why can't I reach the class property from an instance?
I was beating my head on this for quite a while before finding what I believe is the answer. Python #properties are a subset of descriptors, and, from the descriptor documentation (emphasis mine):
The default behavior for attribute access is to get, set, or delete the
attribute from an object’s dictionary. For instance, a.x has a lookup chain
starting with a.__dict__['x'], then type(a).__dict__['x'], and continuing
through the base classes of type(a) excluding metaclasses.
So the method resolution order doesn't include our class properties (or anything else defined in the metaclass). It is possible to make a subclass of the built-in property decorator that behaves differently, but (citation needed) I've gotten the impression googling that the developers had a good reason (which I do not understand) for doing it that way.
That doesn't mean we're out of luck; we can access the properties on the class itself just fine...and we can get the class from type(self) within the instance, which we can use to make #property dispatchers:
class Foo(object, metaclass=MetaFoo):
_thingy = 23
#property
def thingy(self):
return type(self).thingy
Now Foo().thingy works as intended for both the class and the instances! It will also continue to do the right thing if a derived class replaces its underlying _thingy (which is the use case that got me on this hunt originally).
This isn't 100% satisfying to me -- having to do setup in both the metaclass and object class feels like it violates the DRY principle. But the latter is just a one-line dispatcher; I'm mostly okay with it existing, and you could probably compact it down to a lambda or something if you really wanted.
If you use Django, it has a built in #classproperty decorator.
from django.utils.decorators import classproperty
For Django 4, use:
from django.utils.functional import classproperty
I think you may be able to do this with the metaclass. Since the metaclass can be like a class for the class (if that makes sense). I know you can assign a __call__() method to the metaclass to override calling the class, MyClass(). I wonder if using the property decorator on the metaclass operates similarly.
Wow, it works:
class MetaClass(type):
def getfoo(self):
return self._foo
foo = property(getfoo)
#property
def bar(self):
return self._bar
class MyClass(object):
__metaclass__ = MetaClass
_foo = 'abc'
_bar = 'def'
print MyClass.foo
print MyClass.bar
Note: This is in Python 2.7. Python 3+ uses a different technique to declare a metaclass. Use: class MyClass(metaclass=MetaClass):, remove __metaclass__, and the rest is the same.
As far as I can tell, there is no way to write a setter for a class property without creating a new metaclass.
I have found that the following method works. Define a metaclass with all of the class properties and setters you want. IE, I wanted a class with a title property with a setter. Here's what I wrote:
class TitleMeta(type):
#property
def title(self):
return getattr(self, '_title', 'Default Title')
#title.setter
def title(self, title):
self._title = title
# Do whatever else you want when the title is set...
Now make the actual class you want as normal, except have it use the metaclass you created above.
# Python 2 style:
class ClassWithTitle(object):
__metaclass__ = TitleMeta
# The rest of your class definition...
# Python 3 style:
class ClassWithTitle(object, metaclass = TitleMeta):
# Your class definition...
It's a bit weird to define this metaclass as we did above if we'll only ever use it on the single class. In that case, if you're using the Python 2 style, you can actually define the metaclass inside the class body. That way it's not defined in the module scope.
def _create_type(meta, name, attrs):
type_name = f'{name}Type'
type_attrs = {}
for k, v in attrs.items():
if type(v) is _ClassPropertyDescriptor:
type_attrs[k] = v
return type(type_name, (meta,), type_attrs)
class ClassPropertyType(type):
def __new__(meta, name, bases, attrs):
Type = _create_type(meta, name, attrs)
cls = super().__new__(meta, name, bases, attrs)
cls.__class__ = Type
return cls
class _ClassPropertyDescriptor(object):
def __init__(self, fget, fset=None):
self.fget = fget
self.fset = fset
def __get__(self, obj, owner):
if self in obj.__dict__.values():
return self.fget(obj)
return self.fget(owner)
def __set__(self, obj, value):
if not self.fset:
raise AttributeError("can't set attribute")
return self.fset(obj, value)
def setter(self, func):
self.fset = func
return self
def classproperty(func):
return _ClassPropertyDescriptor(func)
class Bar(metaclass=ClassPropertyType):
__bar = 1
#classproperty
def bar(cls):
return cls.__bar
#bar.setter
def bar(cls, value):
cls.__bar = value
bar = Bar()
assert Bar.bar==1
Bar.bar=2
assert bar.bar==2
nbar = Bar()
assert nbar.bar==2
I happened to come up with a solution very similar to #Andrew, only DRY
class MetaFoo(type):
def __new__(mc1, name, bases, nmspc):
nmspc.update({'thingy': MetaFoo.thingy})
return super(MetaFoo, mc1).__new__(mc1, name, bases, nmspc)
#property
def thingy(cls):
if not inspect.isclass(cls):
cls = type(cls)
return cls._thingy
#thingy.setter
def thingy(cls, value):
if not inspect.isclass(cls):
cls = type(cls)
cls._thingy = value
class Foo(metaclass=MetaFoo):
_thingy = 23
class Bar(Foo)
_thingy = 12
This has the best of all answers:
The "metaproperty" is added to the class, so that it will still be a property of the instance
Don't need to redefine thingy in any of the classes
The property works as a "class property" in for both instance and class
You have the flexibility to customize how _thingy is inherited
In my case, I actually customized _thingy to be different for every child, without defining it in each class (and without a default value) by:
def __new__(mc1, name, bases, nmspc):
nmspc.update({'thingy': MetaFoo.services, '_thingy': None})
return super(MetaFoo, mc1).__new__(mc1, name, bases, nmspc)
If you only need lazy loading, then you could just have a class initialisation method.
EXAMPLE_SET = False
class Example(object):
#classmethod
def initclass(cls):
global EXAMPLE_SET
if EXAMPLE_SET: return
cls.the_I = 'ok'
EXAMPLE_SET = True
def __init__( self ):
Example.initclass()
self.an_i = 20
try:
print Example.the_I
except AttributeError:
print 'ok class not "loaded"'
foo = Example()
print foo.the_I
print Example.the_I
But the metaclass approach seems cleaner, and with more predictable behavior.
Perhaps what you're looking for is the Singleton design pattern. There's a nice SO QA about implementing shared state in Python.
I want to create a decorator that works like a property, only it calls the decorated function only once, and on subsequent calls always return the result of the first call. An example:
def SomeClass(object):
#LazilyInitializedProperty
def foo(self):
print "Now initializing"
return 5
>>> x = SomeClass()
>>> x.foo
Now initializing
5
>>> x.foo
5
My idea was to write a custom decorator for this. So i started, and this is how far I came:
class LazilyInitializedProperty(object):
def __init__(self, function):
self._function = function
def __set__(self, obj, value):
raise AttributeError("This property is read-only")
def __get__(self, obj, type):
# problem: where to store the value once we have calculated it?
As you can see, I do not know where to store the cached value. The simplest solution seems to be to just maintain a dictionary, but I am wondering if there is a more elegant solution for this.
EDIT Sorry for that, I forgot to mention that I want the property to be read-only.
Denis Otkidach's CachedAttribute is a method decorator which makes attributes lazy (computed once, accessible many). To make it also read-only, I added a __set__ method. To retain the ability to recalculate (see below) I added a __delete__ method:
class ReadOnlyCachedAttribute(object):
'''Computes attribute value and caches it in the instance.
Source: Python Cookbook
Author: Denis Otkidach https://stackoverflow.com/users/168352/denis-otkidach
This decorator allows you to create a property which can be computed once and
accessed many times. Sort of like memoization
'''
def __init__(self, method, name=None):
self.method = method
self.name = name or method.__name__
self.__doc__ = method.__doc__
def __get__(self, inst, cls):
if inst is None:
return self
elif self.name in inst.__dict__:
return inst.__dict__[self.name]
else:
result = self.method(inst)
inst.__dict__[self.name]=result
return result
def __set__(self, inst, value):
raise AttributeError("This property is read-only")
def __delete__(self,inst):
del inst.__dict__[self.name]
For example:
if __name__=='__main__':
class Foo(object):
#ReadOnlyCachedAttribute
# #read_only_lazyprop
def bar(self):
print 'Calculating self.bar'
return 42
foo=Foo()
print(foo.bar)
# Calculating self.bar
# 42
print(foo.bar)
# 42
try:
foo.bar=1
except AttributeError as err:
print(err)
# This property is read-only
del(foo.bar)
print(foo.bar)
# Calculating self.bar
# 42
One of the beautiful things about CachedAttribute (and
ReadOnlyCachedAttribute) is that if you del foo.bar, then the next time you
access foo.bar, the value is re-calculated. (This magic is made possible by
the fact that del foo.bar removes 'bar' from foo.__dict__ but the property
bar remains in Foo.__dict__.)
If you don't need or don't want this ability to recalculate,
then the following (based on Mike Boers' lazyprop) is a simpler way to make a read-only lazy property.
def read_only_lazyprop(fn):
attr_name = '_lazy_' + fn.__name__
#property
def _lazyprop(self):
if not hasattr(self, attr_name):
setattr(self, attr_name, fn(self))
return getattr(self, attr_name)
#_lazyprop.setter
def _lazyprop(self,value):
raise AttributeError("This property is read-only")
return _lazyprop