关于python:如何在不为每个方法反复键入的情况下装饰类的所有函数?

How to decorate all functions of a class without typing it over and over for each method?

本问题已经有最佳答案,请猛点这里访问。

假设我的类有许多方法,并且我希望将decorator应用于其中的每一个方法,稍后当我添加新方法时,我希望应用相同的decorator,但我不想一直在方法声明上方编写@my decorator?

如果我调查一下__call__,这是正确的方法吗?

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

编辑:我想展示这种方式,这是一个类似于我的问题的解决方案,任何一个后来发现这个问题的家伙,使用一个在评论中提到的mixin。

1
2
3
4
5
6
7
8
9
10
11
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)

然后你可以换一个

1
2
3
class Wrapmymethodsaround(WrapinMixin):
    def __call__:
         return super(Wrapmymethodsaround, self).__call__(hey, you, *args)


用一个遍历类属性并修饰可调用文件的函数来修饰类。如果您有可能恰好可以调用的类变量,那么这可能是错误的做法,并且还将修饰嵌套类(这归功于Sven Marnach指出的),但一般来说,这是一个相当干净和简单的解决方案。示例实施(请注意,这不会排除可能需要或可能不需要的特殊方法(__init__等):

1
2
3
4
5
6
7
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

这样使用:

1
2
3
4
5
@for_all_methods(mydecorator)
class C(object):
    def m1(self): pass
    def m2(self, x): pass
    ...

在python 3.0和3.1中,callable不存在。它一直存在于python 2.x中,现在又回到了python 3.2中,作为isinstance(x, collections.Callable)的包装,因此您可以在这些版本中使用它(或者使用它定义自己的callable替换)。


虽然我不喜欢在使用显式方法时使用神奇的方法,但您可能会为此使用元类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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一样。

1
2
3
>>> quux = myClass()
>>> quux.baz()
bar


不是为了让死神复活,但我真的很喜欢德尔南的回答,但我发现它非常缺乏。

1
2
3
4
5
6
7
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

编辑:固定缩进

因此,您可以指定方法//属性//不希望修饰的内容


以上的答案对我来说都不起作用,因为我还想修饰继承的方法,这不是通过使用__dict__来完成的,我不想用元类过度复杂化事情。最后,我很乐意为python 2提供一个解决方案,因为我现在只需要添加一些分析代码来测量类中所有函数使用的时间。

1
2
3
4
5
6
7
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行来完成,请编辑。


您可以生成一个元类。这不会修饰继承的方法。

1
2
3
4
5
6
7
8
9
10
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中使用它,方法如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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