How does the @property decorator work in Python?
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__)
How does property decorator work internally using syntactic sugar(@) in python?
The difference is exactly where you asked yourself if it was (exactly?)
the same.
PEP 318 mentions it rapidly:
Current Syntax
The current syntax for function decorators as
implemented in Python 2.4a2 is:
@dec2
@dec1
def func(arg1, arg2, ...):
pass
This is equivalent to:
def func(arg1, arg2, ...):
pass
func = dec2(dec1(func))
without the intermediate assignment to the variable func. (emphasis mine)
So, that's why everything works fine when you use the @decorator
syntax.
You could make it work by using a different name:
class C:
def __init__(self, language):
self._language = language
def language(self):
return self._language
language = property(language)
def dummy_temporary_name(self, value):
self._language = value
language = language.setter(dummy_temporary_name)
c = C('fr')
print(c.language)
c.language = 'de'
print(c.language)
# fr
# de
Why should I use the @property decorator in python?
The attribute cannot be accessed with dot notation inside the function because that would recursively call the property getter and cause a stack overflow:
class A:
@property
def x(self):
return self.x # StackOverflow
However, the whole point of the @property
is exactly to make it accessible with dot notation. The following are equivalent:
# Without @property
class A:
def x(self):
return 3
a = A()
print(a.x()) # prints 3
# With @property
class A:
@property
def x(self):
return 3
a = A()
print(a.x) # prints 3
Is there an advantage of using the property decorator compared to the property class?
You want to use the decorator, always. There is no advantage to the other syntax, and only disadvantages.
The point of decorators
That's because the decorator syntax was invented specifically to avoid the other syntax. Any examples you find of the name = property(...)
variety is usually in code that predates decorators.
Decorator syntax is syntactic sugar; the form
@decorator
def functionname(...):
# ...
is executed a lot like
def functionname(...):
# ...
functionname = decorator(functionname)
without functionname
being assigned to twice (the def functionname(...)
part creates a function object and assigns to functionname
normally, but with a decorator the function object is created and passed directly to the decorator object).
Python added this feature because when your function body is long, you can't easily see that the function has been wrapped with a decorator. You'd have to scroll down past the function definition to see that, and that's not very helpful when almost everything else you'd want to know about a function is right at the top; the arguments, the name, the docstring are right there.
From the original PEP 318 – Decorators for Functions and Methods specification:
The current method of applying a transformation to a function or method places the actual transformation after the function body. For large functions this separates a key component of the function's behavior from the definition of the rest of the function's external interface.
[...]
This becomes less readable with longer methods. It also seems less than pythonic to name the function three times for what is conceptually a single declaration.
and under Design Goals:
The new syntax should
- [...]
- move from the end of the function, where it's currently hidden, to the front where it is more in your face
So using
@property
def latitude(self):
# ...
@latitude.setter
def latitude(self, latitude):
# ...
is far more readable and self documenting than
def get_latitude(self):
# ...
def set_latitude(self, latitude):
# ...
latitude = property(get_latitude, set_latitude)
No namespace pollution
Next, because the @property
decorator replaces the function object you decorate with the decoration result (a property
instance), you also avoid namespace pollution. Without @property
and @<name>.setter
and @<name>.deleter
, you have to add 3 extra, separate names to your class definition that then no-one will ever use:
>>> [n for n in sorted(vars(Location)) if n[:2] != '__']
['get_latitude', 'get_longitude', 'latitude', 'longitude', 'set_latitude', 'set_longitude']
Imagine a class with 5, or 10 or even more property definitions. Developers less familiar with the project and an auto-completing IDE will surely get confused by the difference between get_latitude
, latitude
and set_latitude
, and you end up with code that mixes styles and makes it harder to now move away from exposing these methods at the class level.
Sure, you can use del get_latitude, set_latitude
right after the latitude = property(...)
assignment, but that's yet more extra code to execute for no real purpose.
Confusing method names
Although you can avoid having to prefix the accessor names with get_
and set_
or otherwise differentiate between the names to create a property()
object from them, that's still how almost all code that doesn't use the @property
decorator syntax ends up naming the accessor methods.
And that can lead to some confusion in tracebacks; an exception raised in one of the accessor methods leads to a traceback with get_latitude
or set_latitude
in the name, while the preceding line used object.latitude
. It may not always be clear to the Python property novice how the two are connected, especially if they missed the latitude = property(...)
line further down; see above.
Accessing to the accessors, how to inherit
You may point out that you may need access to those functions anyway; for example when overriding just the getter or a setter of for the property in a subclass, while inheriting the other accessor.
But the property
object, when accessed on the class, already gives you references to the accessors, via the .fget
, .fset
and .fdel
attributes:
>>> Location.latitude
<property object at 0x10d1c3d18>
>>> Location.latitude.fget
<function Location.get_latitude at 0x10d1c4488>
>>> Location.latitude.fset
<function Location.set_latitude at 0x10d195ea0>
and you can reuse the @<name>.getter
/ @<name>.setter
/ @<name>.deleter
syntax in a subclass without having to remember to create a new property
object!
With the old syntax, it was commonplace to try to override just one of the accessors:
class SpecialLocation(Location):
def set_latitude(self, latitude):
# ...
and then wonder why it would not be picked up by the inherited property
object.
With the decorator syntax, you'd use:
class SpecialLocation(Location):
@Location.latitude.setter
def latitude(self, latitude):
# ...
and the SpecialLocation
subclass then is given a new property()
instance with the getter inherited from Location
, and with a new setter.
TLDR
Use the decorator syntax.
- It is self-documenting
- It avoids namespace pollution
- It makes inheriting accessors from properties cleaner and more straightforward
When using property decorator, should the variable be declared as public or private in __init__?
class B
is better way of doing.
Lets say you have some validation steps in setter
, for example you want to make sure only values in certain range are set to myvar
. class B
will make sure that the validation steps are run even via constructor. However in case of class A
, you are directly updating the variable and in this case the validation steps sitting inside the setter
method are skipped.
class EvenOnly_A:
def __init__(self, myvar):
self._myvar = myvar #
@property
def myvar(self):
return self._myvar
@myvar.setter
def myvar(self, newvar):
assert newvar%2 == 0
self._myvar = newvar
class EvenOnly_B:
def __init__(self, myvar):
self.myvar = myvar
@property
def myvar(self):
return self._myvar
@myvar.setter
def myvar(self, newvar):
assert newvar%2 == 0
self._myvar = newvar
a = EvenOnly_A(3)
b = EvenOnly_B(3)
@property decorator of python not working
Ok, this took me a while to actually find out why the above code was not working in python 2.7.
If you look at the property
documentation for python2.7, you would find that the class that has the property decorators used is actually inheriting object
class and your code doesn't.
Now, when you don't inherit, the property decorator actually doesn't work and setting or getting properties don't work either
(Put a print statements in getter or setter functions and they wont be printed since they were never invoked while setting p.name
or getting p.name
).
Question : So how come get/set for p.name and p.dob works?
Since, you are not inheriting object class in your class, the property decorators are useless, they are not being invoked but have created those property on the Person object.
But, when you use below code, you are explicitly setting those value (without the use of setters), hence thy are printed and p.age never got assigned any value.
p.name = "Andrew"
p.dob = "10-10-1980"
Code Fix : Update your class declaration to -->
class Person(object):
and setters/getters would work (check using print statements) and self.age
would also work.
Bonus : Python3 onwards, all classes, by default, inherit object class.
@property decorator behavior with inheritance in python
@Paul Becotte already provide a good solution to the question I asked. I found one more way (anyway already existing) to access the same functionality which I need here. Because the Derived
class is already derived from Base
class. Hence all the properties of this Base
class is available with Derived
class. So my question was, how to access the property attribute function fun_name
using the Derived
class member function sample_fun
.
My initial idea was to call like this:
Base.fun_name #This was wrong and Paul already gave an explanation on this
@Paul's suggestion was:
Base("ANY NAME").fun_name #This worked and explanation to this also he had given
Another solution of mine (calling with self
since it is derived from Base
):
self.fun_name #This solution also worked since the Derived class is derived from Base
Many thanks !
Python get and set methods versus @property decorator
The best part of using property
for an attribute is that you don't need it.
The philosophy in Python is that classes attributes and methods are all public, but by convention - when you prefix their name with a single "_"
The mechanism behing "property", the descriptor protocol, allows one to change a previous dumb plain attribute into an instrumented attribute, guarded with code for the getter and setter, if the system evolves to a situation where it is needed.
But by default, a name
attribute in a class, is just a plain attribute. You do person.name = "Name"
- no guards needed, no setting method needed nor recommended. When and if it one needs a guard for that (say, to capitalize the name, or filter on improper words), whatever code uses that attribute needs no change: with the use of property, attribute assignment still takes place with the "=" operator.
Other than that, if using "=" does not look prettier than person.set_name("Name")
for you, I think it does for most people. Of course, that is subjective.
Related Topics
How to Add to the Pythonpath in Windows, So It Finds My Modules/Packages
Syntax Error on Print With Python 3
How to Iterate Over a List in Chunks
Replacements For Switch Statement in Python
What Exactly Do "U" and "R" String Prefixes Do, and What Are Raw String Literals
What Is the Purpose of the Single Underscore "_" Variable in Python
How to Keep Keys/Values in Same Order as Declared
How Is Returning the Output of a Function Different from Printing It
How to Sort a List of Dictionaries by a Value of the Dictionary
How to Import a Module Given the Full Path
How to List All Files of a Directory
Imagemagick Not Authorized to Convert Pdf to an Image
Selenium.Common.Exceptions.Invalidselectorexception With "Span:Contains('String')"