如何在不为每种方法反复输入的情况下装饰类的所有函数?

时间:2011-06-10 14:28:43

标签: python wrapper decorator

让我们说我的类有很多方法,我想在每一个方法上应用我的装饰器,以后当我添加新方法时,我想要应用相同的装饰器,但我不想在上面写@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 个答案:

答案 0 :(得分:52)

使用遍历类属性并装饰callables的函数来装饰类。如果你有可能碰巧可调用的类变量,那么这可能是错误的做法,并且还会修饰嵌套类(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
    ...

在Python 3.0和3.1中,callable不存在。它永远存在于Python 2.x中,并且作为isinstance(x, collections.Callable)的包装器返回到Python 3.2中,因此您可以在这些版本中使用它(或使用此定义您自己的callable替换)。

答案 1 :(得分:24)

虽然我不喜欢在显式方法中使用神奇的方法,但你可以使用元类来实现这一点。

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

答案 2 :(得分:8)

不要从死里复活,但我真的很喜欢德尔南的答案,但发现它很缺乏。

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

编辑:修复缩进

所以你可以指定方法//属性//你不想要装饰的东西

答案 3 :(得分: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行中完成,请编辑。

答案 4 :(得分:2)

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

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