Is there a way to apply a decorator to a Python method that needs class information?

When you decorate a method, it is not yet associated with the class, and therefore it does not yet have the im_class attribute. I am looking for a way to get class information inside a decorator. I tried this:

 import types def decorator(method): def set_signal(self, name, value): print name if name == 'im_class': print "I got the class" method.__setattr__ = types.MethodType(set_signal, method) return method class Test(object): @decorator def bar(self, foo): print foo 

But he does not print anything.

I can imagine it:

 class Test(object): @decorator(klass=Test) def bar(self, foo): print foo 

But if I can avoid it, it will make my day.

+4
source share
4 answers

__setattr__ is called only with explicit assignments object.attribute = ; building a class does not use attribute assignment, but instead creates a dictionary ( Test.__dict__ ).

To access the class, you have several different options:

  • Use the class decorator instead; it will be transferred to the completed class after its creation, you can decorate individual methods in this class by replacing them (decorated) in the class. You can use a combination of a function decorator and a class decorator to mark which methods should be styled:

     def methoddecoratormarker(func): func._decorate_me = True return func def realmethoddecorator(func): # do something with func. # Note: it is still an unbound function here, not a method! return func def classdecorator(klass): for name, item in klass.__dict__.iteritems(): if getattr(item, '_decorate_me', False): klass.__dict__[name] = realmethoddecorator(item) 

    You can use a metaclass instead of a class decorator to achieve the same, of course.

  • Trick and use sys._getframe() to extract the class from the calling frame:

     import sys def methoddecorator(func): callingframe = sys._getframe(1) classname = callingframe.f_code.co_name 

    Note that all you can get is a class name; the class itself is still under construction at this time. You can add elements to callingframe.f_locals (matching) and they will be included in the new class object.

  • Access to self when calling a method. self is a reference to the instance after all, and self.__class__ will be at least the subclass of the original class in which the function was defined.

+3
source

My strict answer would be: This is not possible because the class does not yet exist when the decorator is executed.

A longer answer will depend on your very exact requirements. As I wrote, you cannot access the class if it does not already exist. One solution would be to later tag the decorated method. Then use the metaclass or class decorator to apply your modifications after creating the class.

Another option involves magic. Look at the implements method implements in zope.interfaces . It has some access to the class information that has just been parsed. I don't know if this will be enough for your use.

+1
source

You can take a look at the descriptors . They allow you to implement __get__ , which is used when accessing the attribute, and can return different things depending on the object and its type.

0
source

Use method decorators to add some marker attributes to interesting methods and use a metaclass that iterates over methods, finds marker attributes, and executes logic. The metaclass code runs when the class is created, so it has a link to the newly created class.

 class MyMeta(object): def __new__(...): ... cls = ... ... iterate over dir(cls), find methods having .is_decorated, act on them return cls def decorator(f): f.is_decorated = True return f class MyBase(object): __metaclass__ = MyMeta class MyClass(MyBase): @decorator def bar(self, foo): print foo 

If you are worried that the MyClass programmer forgets to use MyBase , you can force the metaclass to be set in decorator by examining the global list of frames of the calling stack ( sys._getframe() ).

0
source

Source: https://habr.com/ru/post/1438370/


All Articles