如何为类的所有方法装饰而不重复输入?
假设我的类里面有很多方法,我想在每个方法上都加上一个装饰器。以后如果我再添加新的方法,我也希望这个装饰器能自动应用,但我不想每次都在方法前面写 @mydecorator
。
我在想,查看 __call__
这个方法是不是解决这个问题的好办法呢?
我想展示一种方法,这个方法和我的问题类似,供以后有同样疑问的人参考,使用了一种叫做 mixin 的方式,正如评论中提到的。
class WrapinMixin(object):
def __call__(self, hey, you, *args):
print 'entering', hey, you, repr(args)
try:
ret = getattr(self, hey)(you, *args)
return ret
except:
ret = str(e)
raise
finally:
print 'leaving', hey, repr(ret)
然后你可以在另一个地方
class Wrapmymethodsaround(WrapinMixin):
def __call__:
return super(Wrapmymethodsaround, self).__call__(hey, you, *args)
编辑注:这个例子似乎解决的问题和提问的内容不太一样。
5 个回答
18
我不是想重新提起旧事,但我真的很喜欢delnan的回答,不过觉得有一点点不够完整。
def for_all_methods(exclude, decorator):
def decorate(cls):
for attr in cls.__dict__:
if callable(getattr(cls, attr)) and attr not in exclude:
setattr(cls, attr, decorator(getattr(cls, attr)))
return cls
return decorate
补充:修正了缩进问题
所以你可以指定一些方法、属性或者其他东西,不想让它们被装饰。
40
虽然我不太喜欢用一些神奇的方法来解决问题,特别是当有更直接的方法可以用的时候,但你可以考虑使用元类来实现这个功能。
def myDecorator(fn):
fn.foo = 'bar'
return fn
class myMetaClass(type):
def __new__(cls, name, bases, local):
for attr in local:
value = local[attr]
if callable(value):
local[attr] = myDecorator(value)
return type.__new__(cls, name, bases, local)
class myClass(object):
__metaclass__ = myMetaClass
def baz(self):
print self.baz.foo
这样做的效果就像是每个可以调用的东西在 myClass
里都被加上了 myDecorator
的装饰。
>>> quux = myClass()
>>> quux.baz()
bar
101
给这个类加上一个装饰器函数,这个函数会遍历类里的属性,并把可调用的属性进行装饰。如果你的类里面有一些变量恰好是可调用的,这样做可能就不太合适了。此外,这个方法还会装饰嵌套的类(感谢Sven Marnach指出这一点),但总体来说,这是一种相对简单干净的解决方案。下面是一个示例实现(注意,这个方法不会排除特殊方法,比如__init__
等,这可能是你想要的,也可能不是):
def for_all_methods(decorator):
def decorate(cls):
for attr in cls.__dict__: # there's propably a better way to do this
if callable(getattr(cls, attr)):
setattr(cls, attr, decorator(getattr(cls, attr)))
return cls
return decorate
使用方法如下:
@for_all_methods(mydecorator)
class C(object):
def m1(self): pass
def m2(self, x): pass
...