我想将特定类的每个方法包装在python中,并且希望通过最少地编辑该类的代码来实现。我应该怎么做?
Michael Foord的Voidspace博客的一个条目中介绍了一种优雅的实现方法,该条目在“方法修饰元类的方法”部分中介绍了什么是元类以及如何使用它们。稍微简化一下,然后将其应用于您的情况会导致以下结果:
from types import FunctionType from functools import wraps def wrapper(method): @wraps(method) def wrapped(*args, **kwrds): # ... <do something to/with "method" or the result of calling it> return wrapped class MetaClass(type): def __new__(meta, classname, bases, classDict): newClassDict = {} for attributeName, attribute in classDict.items(): if isinstance(attribute, FunctionType): # replace it with a wrapped version attribute = wrapper(attribute) newClassDict[attributeName] = attribute return type.__new__(meta, classname, bases, newClassDict) class MyClass(object): __metaclass__ = MetaClass # wrap all the methods def method1(self, ...): # ...etc ...
在Python中,函数/方法装饰器只是函数包装器和一些语法糖,以使其易于使用(更美观)。
Python 3兼容性更新
先前的代码使用Python 2.x元类语法,需要进行翻译才能在Python 3.x中使用,但是在先前的版本中将不再起作用。这意味着它将需要使用:
class MyClass(metaclass=MetaClass) ...
代替:
class MyClass(object): __metaclass__ = MetaClass" ...
如果需要,可以编写与Python 2.x 和 3.x兼容的代码,但是这样做需要使用稍微复杂一些的技术,该技术可以动态创建一个继承所需元类的新基类,从而避免由于两个Python版本之间的语法差异。这基本上就是本杰明·彼得森的六个模块的with_metaclass()功能。
with_metaclass()
from types import FunctionType from functools import wraps def wrapper(method): @wraps(method) def wrapped(*args, **kwrds): print('{!r} executing'.format(method.__name__)) return method(*args, **kwrds) return wrapped class MetaClass(type): def __new__(meta, classname, bases, classDict): newClassDict = {} for attributeName, attribute in classDict.items(): if isinstance(attribute, FunctionType): # replace it with a wrapped version attribute = wrapper(attribute) newClassDict[attributeName] = attribute return type.__new__(meta, classname, bases, newClassDict) def with_metaclass(meta): """ Create an empty class with the supplied bases and metaclass. """ return type.__new__(meta, "TempBaseClass", (object,), {}) if __name__ == '__main__': # Inherit metaclass from a dynamically-created base class. class MyClass(with_metaclass(MetaClass)): @staticmethod def a_static_method(): pass @classmethod def a_class_method(cls): pass def a_method(self): pass instance = MyClass() instance.a_static_method() # Not decorated. instance.a_class_method() # Not decorated. instance.a_method() # -> 'a_method' executing