admin管理员组

文章数量:1384317

I'm investigating the pattern whereby you have a method decorator which annotates the method in some way, and then once the class is defined it looks through its methods, finds the annotated methods, and registers or processes them in some way. e.g.

def class_decorator(cls):
   for name, method in cls.__dict__.iteritems():
        if hasattr(method, "use_class"):
            # do something with the method and class
            print name, cls
   return cls

def method_decorator(view):
    # mark the method as something that requires view's class
    view.use_class = True
    return view

@class_decorator
class ModelA(object):
    @method_decorator
    def a_method(self):
        # do some stuff
        pass

(From here: )

I wondered if anyone has a solution to the problem of multiple decorators. For example, this example works fine in isolation, but if I do the following, it will break because the use_class annotation will be hidden.

def hiding_decorator(fn):
    def wrapper(*args, **kwargs):
        print("do some logging etc.")
        return fn(*args, **kwargs)
    return wrapper

@class_decorator
class ModelA(object):
    @hiding_decorator
    @method_decorator
    def a_method(self):
        # do some stuff
        pass

Does anyone have a reliable and user-friendly way around this problem?

Non-optimal solutions I can think of:

  • Dictate that your decorator should be outermost (but what if other decorators also have this requirement?)
  • Dictate that it only works with decorators that are transparent proxies for attributes of their inner functions/methods.
  • Require other decorators to be aware of the requirements of the tagging decorator (means generic utility decorators become difficult to use).

None of these are particularly attractive.

I'm investigating the pattern whereby you have a method decorator which annotates the method in some way, and then once the class is defined it looks through its methods, finds the annotated methods, and registers or processes them in some way. e.g.

def class_decorator(cls):
   for name, method in cls.__dict__.iteritems():
        if hasattr(method, "use_class"):
            # do something with the method and class
            print name, cls
   return cls

def method_decorator(view):
    # mark the method as something that requires view's class
    view.use_class = True
    return view

@class_decorator
class ModelA(object):
    @method_decorator
    def a_method(self):
        # do some stuff
        pass

(From here: https://stackoverflow/a/2367605/1256529)

I wondered if anyone has a solution to the problem of multiple decorators. For example, this example works fine in isolation, but if I do the following, it will break because the use_class annotation will be hidden.

def hiding_decorator(fn):
    def wrapper(*args, **kwargs):
        print("do some logging etc.")
        return fn(*args, **kwargs)
    return wrapper

@class_decorator
class ModelA(object):
    @hiding_decorator
    @method_decorator
    def a_method(self):
        # do some stuff
        pass

Does anyone have a reliable and user-friendly way around this problem?

Non-optimal solutions I can think of:

  • Dictate that your decorator should be outermost (but what if other decorators also have this requirement?)
  • Dictate that it only works with decorators that are transparent proxies for attributes of their inner functions/methods.
  • Require other decorators to be aware of the requirements of the tagging decorator (means generic utility decorators become difficult to use).

None of these are particularly attractive.

Share Improve this question asked Mar 18 at 8:21 samfrancessamfrances 3,7673 gold badges28 silver badges48 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 3

Since a "tag" is always applied on a function object itself, it is always going to be susceptible to obstruction by an unaware wrapper function.

To achieve a similar usage you can implement the behavior with a registry pattern instead by adding method names to a list with a method decorator so that the class decorator can obtain the final decorated function object with getattr:

class Registry:
    def __init__(self):
        self.names = []

    def register(self, func):
        self.names.append(func.__name__)
        return func

    def apply(self, cls):
        for name in self.names:
            print(cls, name)
            print(getattr(cls, name))

registry = Registry()
@registry.apply
class ModelA(object):
    @hiding_decorator
    @registry.register
    def a_method(self):
        pass

This outputs something like:

<class '__main__.ModelA'> a_method
<function hiding_decorator.<locals>.wrapper at 0x1551795d67a0>

Demo: https://ideone/R43Izy

本文标签: pythonMethod decorators which quottagquot methodprevent overwriting by other decoratorsStack Overflow