我想用相同的包装器在 Python 中包装一些类方法。
从概念上讲,在最简单的场景中它看起来像这样:
x = 0 # some arbitrary context
class Base(object):
def a(self):
print "a x: %s" % x
def b(self):
print "b x: %s" % x
class MixinWithX(Base):
"""Wrap"""
def a(self):
global x
x = 1
super(MixinWithX, self).a()
x = 0
def b(self):
global x
x = 1
super(MixinWithX, self).a()
x = 0
当然,当方法多于a
andb
时,这会变得一团糟。似乎应该有更简单的东西。显然x
可以在装饰器中进行修改,但最终仍然会有一长串垃圾,而不是上面的样子:
from functools import wraps
def withx(f):
@wraps(f) # good practice
def wrapped(*args, **kwargs):
global x
x = 1
f(*args, **kwargs)
x = 0
return wrapped
class MixinWithX(Base):
"""Wrap"""
@withx
def a(self):
super(MixinWithX, self).a()
@withx
def b(self):
super(MixinWithX, self).b()
我考虑过__getattr__
在 mixin 中使用,但是当然,因为已经定义了a
和之类的方法,所以永远不会调用它。b
我也考虑过使用__getattribute__
,但它返回属性,而不是包装调用。我想__getattribute__
可以返回一个闭包(下面的示例),但我不确定设计有多合理。这是一个例子:
class MixinWithX(Base):
# a list of the methods of our parent class (Base) that are wrapped
wrapped = ['a', 'b']
# application of the wrapper around the methods specified
def __getattribute__(self, name):
original = object.__getattribute__(self, name)
if name in wrapped:
def wrapped(self, *args, **kwargs):
global x
x = 1 # in this example, a context manager would be handy.
ret = original(*args, **kwargs)
x = 0
return ret
return wrapped
return original
我突然想到,Python 中可能内置了一些东西,可以减轻手动重现要包装的父类的每个方法的需要。或者,也许关闭__getattribute__
是做到这一点的正确方法。我会很感激的想法。