如何装饰一个类的所有函数,而不用为每个方法一遍又一遍地输入它?

假设我的类有很多方法,我想在每一个方法上都应用修饰符,稍后当我添加新方法时,我希望应用相同的修饰符,但是我不想一直在方法声明上面写@mydecator?

如果我调查 __call__是正确的方法吗?

重要提示: 下面的例子似乎解决了一个与原问题不同的问题。

编辑: Id like to show this way,这是一个类似于我的问题的解决方案,任何人以后发现这个问题时都可以使用,就像评论中提到的混合。

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)
49119 次浏览

Decorate the class with a function that walks through the class's attributes and decorates callables. This may be the wrong thing to do if you have class variables that may happen to be callable, and will also decorate nested classes (credits to Sven Marnach for pointing this out) but generally it's a rather clean and simple solution. Example implementation (note that this will not exclude special methods (__init__ etc.), which may or may not be desired):

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

Use like this:

@for_all_methods(mydecorator)
class C(object):
def m1(self): pass
def m2(self, x): pass
...

In Python 3.0 and 3.1, callable does not exist. It existed since forever in Python 2.x and is back in Python 3.2 as wrapper for isinstance(x, collections.Callable), so you can use that (or define your own callable replacement using this) in those versions.

While I'm not fond of using magical approaches when an explicit approach would do, you can probably use a metaclass for this.

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

and it works as though each callable in myClass had been decorated with myDecorator

>>> quux = myClass()
>>> quux.baz()
bar

You could generate a metaclass. This will not decorate inherited methods.

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

This will generate a metaclass decorating all methods with the specified function. You can use it in Python 2 or 3 by doing something like this

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

Not to revive things from the dead, but I really liked delnan's answer, but found it sllliigghhtttlllyy lacking.

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

EDIT: fix indenting

So you can specify methods//attributes//stuff you don't want decorated

None of the above answers worked for me, since I wanted to also decorate the inherited methods, which was not accomplished by using __dict__, and I did not want to overcomplicate things with metaclasses. Lastly, I am fine with having a solution for Python 2, since I just have an immediate need to add some profiling code for measuring time used by all functions of a class.

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

Source (slightly different solution): https://stackoverflow.com/a/3467879/1243926 There you can also see how to change it for Python 3.

As comments to other answers suggest, consider using inspect.getmembers(cls, inspect.isroutine) instead. If you have found a proper solution that works for both Python 2 and Python 3 and decorates inherited methods, and can still be done in 7 lines, please, edit.