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.com/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:
None of these are particularly attractive.
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>