如何装饰一个类的所有函数,而不用为每个方法反复输入?[重复]
- 2025-03-04 08:27:00
- admin 原创
- 70
问题描述:
假设我的类有很多方法,我想在每个方法上应用我的装饰器,以后当我添加新方法时,我希望应用相同的装饰器,但我不想@mydecorator
一直在方法声明上面写。
如果我研究一下,__call__
这是正确的做法吗?
我想展示这种方法,对于以后发现此问题的任何人来说,这都是解决我的问题的类似方法,使用评论中提到的混合器。
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__(self, hey, you, *args):
return super(Wrapmymethodsaround, self).__call__(hey, you, *args)
编者注:这个例子似乎解决的问题与所问的问题不同。
解决方案 1:
使用遍历类的属性并装饰可调用函数来装饰类。如果您有可调用的类变量,这可能是错误的做法,并且还会装饰嵌套类(感谢 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
...
解决方案 2:
尽管我不喜欢在使用明确的方法时使用神奇的方法,但您可能可以为此使用元类。
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
解决方案 3:
不是为了起死回生,但我真的很喜欢德尔南的回答,但发现它确实有些欠缺。
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
编辑:修复缩进
因此,您可以指定方法//属性//您不想装饰的东西
解决方案 4:
上述答案对我都不起作用,因为我还想修饰继承的方法,这无法通过使用来实现__dict__
,而且我不想用元类让事情变得过于复杂。最后,我很乐意为 Python 2 提供一个解决方案,因为我只需要立即添加一些分析代码来测量类中所有函数所用的时间。
import inspect
def for_all_methods(decorator):
def decorate(cls):
for name, fn in inspect.getmembers(cls, inspect.ismethod):
setattr(cls, name, decorator(fn))
return cls
return decorate
来源(略有不同的解决方案):https://stackoverflow.com/a/3467879/1243926
您还可以在那里看到如何为 Python 3 进行更改。
正如对其他答案的评论所建议的那样,请考虑使用inspect.getmembers(cls, inspect.isroutine)
。如果您找到了一种适用于 Python 2 和 Python 3 并修饰继承方法的适当解决方案,并且仍然可以在 7 行内完成,请进行编辑。
解决方案 5:
您可以生成一个元类。这不会修饰继承的方法。
def decorating_meta(decorator):
class DecoratingMetaclass(type):
def __new__(self, class_name, bases, namespace):
for key, value in list(namespace.items()):
if callable(value):
namespace[key] = decorator(value)
return type.__new__(self, class_name, bases, namespace)
return DecoratingMetaclass
这将生成一个元类,用指定的函数装饰所有方法。你可以在 Python 2 或 3 中使用它,操作如下
def doubling_decorator(f):
def decorated(*a, **kw):
return f(*a, **kw) * 2
return decorated
class Foo(dict):
__metaclass__ = decorating_meta(doubling_decorator)
def lookup(self, key):
return self[key]
d = Foo()
d["bar"] = 5
print(d.lookup("bar")) # prints 10
扫码咨询,免费领取项目管理大礼包!