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(object):
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 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(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
And, how are the x.setter
and x.deleter
decorators created?
I am confused.
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:-
The calling of function "get_config()" will work like this.
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.
Documentation says it's just a shortcut for creating readonly properties. So
is equivalent to
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:and an user creates a library depending on this class where he/she uses e.g.
Now let's suppose you decide to change your
Money
class and get rid of thedollars
andcents
attributes but instead decide to only track the total amount of cents:If the above mentioned user now tries to run his/her library as before
it will result in an error
That means that now everyone who relies on your original
Money
class would have to change all lines of code wheredollars
andcents
are used which can be very painful... So, how could this be avoided? By using@property
!That is how:
when we now call from our library
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:You can use
@property
also in abstract classes; I give a minimal example here.Here is another example:
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:
and if I comment out the self.x = 1234 in init then the output is:
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 ):
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
The
property()
function returns a special descriptor object:It is this object that has extra methods:
These act as decorators too. They return a new property object:
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:really means the same thing as
so
foo
the function is replaced byproperty(foo)
, which we saw above is a special object. Then when you use@foo.setter()
, what you are doing is call thatproperty().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:Next we use the
.setter()
method to add a setter:Last we add a deleter with the
.deleter()
method: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:The Descriptor Howto includes a pure Python sample implementation of the
property()
type: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
:In this code we've "hidden" attribute
password
by using@property
which triggersAttributeError
assertion when you try to access it directly, while we used @property.setter to set the actual instance variablepassword_hash
.Now in
auth/views.py
we can instantiate a User with:Notice attribute
password
that comes from a registration form when a user fills the form. Password confirmation happens on the front end withEqualTo('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