如何包装类的每个方法?[重复]
- 2025-04-10 09:45:00
- admin 原创
- 16
问题描述:
我想用 Python 包装某个特定类的每个方法,并且希望通过最少地编辑该类的代码来实现这一点。我应该怎么做?
解决方案 1:
Michael Foord 的 Voidspace 博客在一篇关于什么是元类以及如何在标题为“方法装饰元类”的部分中使用元类的条目中描述了一种优雅的方法 。 稍微简化一下并将其应用到您的情况,结果是这样的:
from functools import wraps
from types import FunctionType
def wrapper(method):
@wraps(method)
def wrapped(*args, **kwargs):
# ... <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) # apply method-wrapping metaclass
...
而不是:
class MyClass(object):
__metaclass__ = MetaClass # wrap all the methods
...
如果需要,可以编写与 Python 2.x和3.x 兼容的代码,但这样做需要使用稍微复杂的技术,即动态创建一个继承所需元类的新基类,从而避免由于两个版本的 Python 之间的语法差异而导致的错误。这基本上就是 Benjamin Peterson 的six模块的with_metaclass()
功能。
from types import FunctionType
from functools import wraps
def wrapper(method):
@wraps(method)
def wrapped(*args, **kwargs):
print('{!r} executing'.format(method.__name__))
return method(*args, **kwargs)
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
解决方案 2:
您的意思是通过编程将包装器设置为类的方法?嗯,这可能是一个非常糟糕的做法,但您可以这样做:
def wrap_methods( cls, wrapper ):
for key, value in cls.__dict__.items( ):
if hasattr( value, '__call__' ):
setattr( cls, key, wrapper( value ) )
例如,如果你有课,
class Test( ):
def fire( self ):
return True
def fire2( self ):
return True
和一个包装纸
def wrapper( fn ):
def result( *args, **kwargs ):
print 'TEST'
return fn( *args, **kwargs )
return result
然后调用
wrap_methods( Test, wrapper )
将应用于wrapper
类中定义的所有Test
方法。请谨慎使用!实际上,根本不要使用它!
解决方案 3:
如果需要大量修改默认类行为,那么 MetaClasses 是最佳选择。以下是另一种方法。
如果您的用例仅限于包装类的实例方法,您可以尝试重写__getattribute__
魔术方法。
from functools import wraps
def wrapper(func):
@wraps(func)
def wrapped(*args, **kwargs):
print "Inside Wrapper. calling method %s now..."%(func.__name__)
return func(*args, **kwargs)
return wrapped
确保functools.wraps
在创建包装器时使用,如果包装器用于调试则更是如此,因为它提供了合理的 TraceBacks。
import types
class MyClass(object): # works only for new-style classes
def method1(self):
return "Inside method1"
def __getattribute__(self, name):
attr = super(MyClass, self).__getattribute__(name)
if type(attr) == types.MethodType:
attr = wrapper(attr)
return attr
相关推荐
热门文章
项目管理软件有哪些?
热门标签
曾咪二维码
扫码咨询,免费领取项目管理大礼包!
云禅道AD