class A:
@property
def p(self): return 2
def q(self): return 2
a = A()
A.p(a) #>> TypeError: 'property' object is not callable
A.q(a) #>> no error, returns 2
Why is this? I understand if I referred to the property on an instance : a.p would simply return the method return value, but I am trying to start with the property on the class. I would have expected no error above, with both evaluating to 2.
Python's property() is the Pythonic way to avoid formal getter and setter methods in your code. This function allows you to turn class attributes into properties or managed attributes.
The @property is a built-in decorator for the property() function in Python. It is used to give "special" functionality to certain methods to make them act as getters, setters, or deleters when we define properties in a class.
Getters and Setters in python are often used when: We use getters & setters to add validation logic around getting and setting a value. To avoid direct access of a class field i.e. private variables cannot be accessed directly or modified by external user.
fget is a function for getting an attribute value, likewise fset is a function for setting, and fdel a function for del'ing, an attribute. Typical use is to define a managed attribute x: class C(object): def __init__(self): self.
You're digging into the world of descriptors
. A.p
is a property
and properties are descriptors. It's a class that has magic methods (__get__
, __set__
...) which get called when the descriptor is accessed on an instance. The particular method accessed depends on how it's accessed of course. Accessing a descriptor on a class simply returns the descriptor itself and no magic is performed -- In this case, the property
descriptor isn't callable so you get an error.
Notice what happens if you call __get__
:
class A(object):
@property
def p(self):
return 2
a = A()
print (A.p.__get__(a)) #2
foo = A.p.__get__(a)
is what actually happens under the hood when you do foo = a.p
. I think that's pretty spiffy...
Because properties aren't callable:
In [3]: class A(object):
...: @property
...: def p(self): return 2
...:
In [4]: A.p
Out[4]: <property at 0x2d919a8>
In [5]: dir(A.p)
Out[5]:
['__class__',
'__delattr__',
'__delete__',
'__doc__',
'__format__',
'__get__',
'__getattribute__',
'__hash__',
'__init__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__set__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'deleter',
'fdel',
'fget',
'fset',
'getter',
'setter']
Note the lack of a __call__
method. This is because properties can wrap up more than one function.
Here's what happens if you try to invoke the property as a method on an instance:
In [6]: a = A()
In [7]: a.p()
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
C:\Users\Marcin\<ipython-input-7-16c8de382321> in <module>()
----> 1 a.p()
TypeError: 'int' object is not callable
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With