Nesting descriptors/decorators in python

2024/9/16 23:21:07

I'm having a hard time understanding what happens when I try to nest descriptors/decorators. I'm using python 2.7.

For example, let's take the following simplified versions of property and classmethod:

class MyProperty(object):def __init__(self, fget):self.fget = fgetdef __get__(self, obj, objtype=None):print 'IN MyProperty.__get__'return self.fget(obj)class MyClassMethod(object):def __init__(self, f):self.f = fdef __get__(self, obj, objtype=None):print 'IN MyClassMethod.__get__'def f(*args, **kwargs):return self.f(objtype, *args, **kwargs)return f

Trying to nest them:

class A(object):# doesn't work:@MyProperty@MyClassMethoddef klsproperty(cls):return 555# works:@MyPropertydef prop(self):return 111# works:@MyClassMethoddef klsmethod(cls, x):return x**2% print A.klsproperty
IN MyProperty.__get__
...
TypeError: 'MyClassMethod' object is not callable

The __get__ method of the inner descriptor MyClassMethod is not getting called. Failing to figure out why, I tried throwing in (what I think is) a no-op descriptor:

class NoopDescriptor(object):def __init__(self, f):self.f = fdef __get__(self, obj, objtype=None):print 'IN NoopDescriptor.__get__'return self.f.__get__(obj, objtype=objtype)

Trying to use the no-op descriptor/decorator in nesting:

class B(object):# works:@NoopDescriptor@MyPropertydef prop1(self):return 888# doesn't work:@MyProperty@NoopDescriptordef prop2(self):return 999% print B().prop1
IN NoopDescriptor.__get__
IN MyProperty.__get__
888
% print B().prop2
IN MyProperty.__get__
...
TypeError: 'NoopDescriptor' object is not callable

I don't understand why B().prop1 works and B().prop2 does not.

Questions:

  1. What am I doing wrong? Why am I getting a object is not callable error?
  2. What's the right way? e.g. what is the best way to define MyClassProperty while re-using MyClassMethod and MyProperty (or classmethod and property)
Answer

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

https://en.xdnf.cn/q/72794.html

Related Q&A

Retrieve definition for parenthesized abbreviation, based on letter count

I need to retrieve the definition of an acronym based on the number of letters enclosed in parentheses. For the data Im dealing with, the number of letters in parentheses corresponds to the number of w…

Python (Watchdog) - Waiting for file to be created correctly

Im new to Python and Im trying to implement a good "file creation" detection. If I do not put a time.sleep(x) my files are elaborated in a wrong way since they are still being "created&q…

How do I display add model in tabular format in the Django admin?

Im just starting out with Django writing my first app - a chore chart manager for my family. In the tutorial it shows you how to add related objects in a tabular form. I dont care about the related obj…

Python Matplotlib - Impose shape dimensions with Imsave

I plot a great number of pictures with matplotlib in order to make video with it but when i try to make the video i saw the shape of the pictures is not the same in time...It induces some errors. Is th…

Move x-axis tick labels one position to left [duplicate]

This question already has answers here:Aligning rotated xticklabels with their respective xticks(6 answers)Closed last year.I am making a bar chart and I want to move the x-axis tick labels one positio…

PUT dictionary in dictionary in Python requests

I want to send a PUT request with the following data structure:{ body : { version: integer, file_id: string }}Here is the client code:def check_id():id = request.form[id]res = logic.is_id_valid(id)file…

Does python have header files like C/C++? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.Want to improve this question? Add details and clarify the problem by editing this post.Closed 9 years ago.Improve…

python: Greatest common divisor (gcd) for floats, preferably in numpy

I am looking for an efficient way to determine the greatest common divisor of two floats with python. The routine should have the following layoutgcd(a, b, rtol=1e-05, atol=1e-08) """ Re…

Difference between @property and property()

Is there a difference betweenclass Example(object):def __init__(self, prop):self._prop = propdef get_prop(self):return self._propdef set_prop(self, prop):self._prop = propprop = property(get_prop, set_…

airflow webserver command fails with {filesystemcache.py:224} ERROR - Operation not permitted

I am installing airflow on a Cent OS 7. I have configured airflow db init and checked the status of the nginx server as well its working fine. But when I run the airflow webserver command I am getting …