获取类名
如果您想要的只是类名(而不是类本身),它可以作为函数(部分)qualified name attribute (__qualname__) 的一部分使用。
import os.path
def decorator(fn):
filename = fn.__name__
directory = fn.__module__
subdirectory = fn.__qualname__.removesuffix('.' + fn.__name__).replace('.', os.path.sep)
return fn
class A(object):
@decorator
def method(self):
pass
class B(object):
@decorator
def method(self):
pass
如果方法的类是内部类,则 qualname 将包括外部类。上面的代码通过用本地路径分隔符替换所有点分隔符来处理这个问题。
当装饰器被调用时,除了其名称之外的任何内容都无法访问,因为类本身尚未定义。
在方法调用时获取类
如果需要类本身并且可以延迟访问,直到(第一次)调用装饰方法,装饰器可以像往常一样包装函数,然后包装器可以访问实例和类。如果只调用一次,包装器还可以移除自身并取消装饰方法。
import types
def once(fn):
def wrapper(self, *args, **kwargs):
# do something with the class
subdirectory = type(self).__name__
...
# undecorate the method (i.e. remove the wrapper)
setattr(self, fn.__name__, types.MethodType(fn, self))
# invoke the method
return fn(self, *args, **kwargs)
return wrapper
class A(object):
@once
def method(self):
pass
a = A()
a.method()
a.method()
请注意,这仅在调用该方法时才有效。
类定义后获取类
如果即使没有调用修饰方法也需要获取类信息,可以存储对decorator on the wrapper (method #3)的引用,然后扫描所有类的方法(在定义感兴趣的类之后)以查找那些参考装饰器:
def decorator(fn):
def wrapper(self, *args, **kwargs):
return fn(self, *args, **kwargs)
wrapper.__decorator__ = decorator
wrapper.__name__ = 'decorator + ' + fn.__name__
wrapper.__qualname__ = 'decorator + ' + fn.__qualname__
return wrapper
def methodsDecoratedBy(cls, decorator):
for method in cls.__dict__.values():
if hasattr(method, '__decorator__') \
and method.__decorator__ == decorator:
yield method
#...
import sys, inspect
def allMethodsDecoratedBy(decorator)
for name, cls in inspect.getmembers(sys.modules, lambda x: inspect.isclass(x)):
for method in methodsDecoratedBy(cls, decorator):
yield method
这基本上使装饰器成为一般编程意义上的注释(而不是function annotations in Python 的意义上,它们仅用于函数参数和返回值)。一个问题是装饰器必须是最后应用的,否则类属性不会存储相关的包装器,而是另一个外部包装器。这可以通过在包装器上存储(并稍后检查)所有装饰器来部分解决:
def decorator(fn):
def wrapper(self, *args, **kwargs):
return fn(self, *args, **kwargs)
wrapper.__decorator__ = decorator
if not hasattr(fn, '__decorators__'):
if hasattr(fn, '__decorator__'):
fn.__decorators__ = [fn.__decorator__]
else:
fn.__decorators__ = []
wrapper.__decorators__ = [decorator] + fn.__decorators__
wrapper.__name__ = 'decorator(' + fn.__name__ + ')'
wrapper.__qualname__ = 'decorator(' + fn.__qualname__ + ')'
return wrapper
def methodsDecoratedBy(cls, decorator):
for method in cls.__dict__.values():
if hasattr(method, '__decorators__') and decorator in method.__decorators__:
yield method
此外,您无法控制的任何装饰器都可以通过装饰它们进行合作,以便它们将自己存储在包装器中,就像decorator 所做的那样:
def bind(*values, **kwvalues):
def wrap(fn):
def wrapper(self, *args, **kwargs):
nonlocal kwvalues
kwvalues = kwvalues.copy()
kwvalues.update(kwargs)
return fn(self, *values, *args, **kwvalues)
wrapper.__qualname__ = 'bind.wrapper'
return wrapper
wrap.__qualname__ = 'bind.wrap'
return wrap
def registering_decorator(decorator):
def wrap(fn):
decorated = decorator(fn)
decorated.__decorator__ = decorator
if not hasattr(fn, '__decorators__'):
if hasattr(fn, '__decorator__'):
fn.__decorators__ = [fn.__decorator__]
else:
fn.__decorators__ = []
if not hasattr(decorated, '__decorators__'):
decorated.__decorators__ = fn.__decorators__.copy()
decorated.__decorators__.insert(0, decorator)
decorated.__name__ = 'reg_' + decorator.__name__ + '(' + fn.__name__ + ')'
decorated.__qualname__ = decorator.__qualname__ + '(' + fn.__qualname__ + ')'
return decorated
wrap.__qualname__ = 'registering_decorator.wrap'
return wrap
class A(object):
@decorator
def decorated(self):
pass
@bind(1)
def add(self, a, b):
return a + b
@registering_decorator(bind(1))
@decorator
def args(self, *args):
return args
@decorator
@registering_decorator(bind(a=1))
def kwargs(self, **kwargs):
return kwargs
A.args.__decorators__
A.kwargs.__decorators__
assert not hasattr(A.add, '__decorators__')
a = A()
a.add(2)
# 3
另一个问题是扫描所有类效率低下。您可以通过使用额外的类装饰器来注册所有类以检查方法装饰器来提高效率。但是,这种方法很脆弱。如果忘记装饰类,则不会记录在注册表中。
class ClassRegistry(object):
def __init__(self):
self.registry = {}
def __call__(self, cls):
self.registry[cls] = cls
cls.__decorator__ = self
return cls
def getRegisteredClasses(self):
return self.registry.values()
class DecoratedClassRegistry(ClassRegistry):
def __init__(self, decorator):
self.decorator = decorator
super().__init__()
def isDecorated(self, method):
return ( hasattr(method, '__decorators__') \
and self.decorator in method.__decorators__) \
or ( hasattr(method, '__decorator__') \
and method.__decorator__ == self.decorator)
def getDecoratedMethodsOf(self, cls):
if cls in self.registry:
for method in cls.__dict__.values():
if self.isDecorated(method):
yield method
def getAllDecoratedMethods(self):
for cls in self.getRegisteredClasses():
for method in self.getDecoratedMethodsOf(cls):
yield method
用法:
decoratedRegistry = DecoratedClassRegistry(decorator)
@decoratedRegistry
class A(object):
@decoratedRegistry
class B(object):
@decorator
def decorated(self):
pass
def func(self):
pass
@decorator
def decorated(self):
pass
@bind(1)
def add(self, a, b):
return a + b
@registering_decorator(bind(1))
@decorator
def args(self, *args):
return args
@decorator
@registering_decorator(bind(a=1))
def kwargs(self, **kwargs):
return kwargs
decoratedRegistry.getRegisteredClasses()
list(decoratedRegistry.getDecoratedMethodsOf(A.B))
list(decoratedRegistry.getDecoratedMethodsOf(A))
list(decoratedRegistry.getAllDecoratedMethods())
监控多个装饰器并应用多个装饰器注册表作为练习。