Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is "aClass.aProperty" not callable?

Tags:

python

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.

like image 931
Des Avatar asked Nov 13 '12 21:11

Des


People also ask

What is a property in a Python class?

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.

What does @property mean Python?

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.

Should I use getters and setters in python?

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.

What is FGET in Python?

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.


2 Answers

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...

like image 77
mgilson Avatar answered Oct 11 '22 14:10

mgilson


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
like image 36
Marcin Avatar answered Oct 11 '22 13:10

Marcin