In this case, when the decorators are used without parameters, a decorator is called with the function it decorates as its parameter. The decorator's return value is used instead of the decorated function. So:
@MyProperty
def prop(self):
...
is equivalent to:
def prop(self):
...
prop = MyProperty(prop)
Since MyProperty
implements the descriptor protocol, accessing A.prop
will actually call A.prop.__get__()
, and you've defined __get__
to call the object which was decorated (in this case, the original function/method), so everything works fine.
Now, in the nested case:
@MyProperty
@MyClassMethod
def prop(self):
...
The equivalent is:
def prop(self):
...
prop = MyClassMethod(prop) # prop is now instance of MyClassMethod
prop = MyProperty(prop) # prop is now instance of MyProperty
# (with fget == MyClassMethod instance)
Now, as before, accessing A.prop
will actually call A.prop.__get__()
(in MyProperty
) which then tries to call the instance of MyClassMethod
(the object which was decorated and stored in the fget
attribute).
But the MyClassMethod
does not have a __call__
method defined, so you get the error MyClassMethod is not callable
.
And to address your second question: A property is already a class attribute - in your example, accessing A.prop
will return the value of the property in the class object and A().prop
will return the value of the property in an instance object (which can be the same as the class object if the instance did not override it).