如何将装饰器应用于超类的方法?

时间:2014-02-20 00:20:32

标签: python decorator metaclass

假设我有一个python类

class Foo(object):
    def method1(self, x):
        print("method1 called with %s" %(x,))

    def method2(self, x):
        print("method2 called with %s" %(x,))

和装饰者

def myDecorator(func):
    newFunc = someModificationOf(func)
    return newFunc

我希望能够继承Foo,并且在子类定义中以某种方式表明我希望将myDecorator应用于从Foo继承的方法的某个子集。例如,我的子类可能喜欢

class Baz(Foo):
    methodsToDecorate = ['method2']

这可能吗?

我尝试了什么:

我以为我可以用元类做到这一点:

class Baz(Foo):
    __metaclass__ = Meta
    methodsToDecorate = ['method2']

class Meta(type):
    def __new__(cls, name, bases, d):
        for m in d['methodsToDecorate']:
            d[m] = myDecorator(d[m])
        return type(name, bases, d)

但是,它不起作用,因为'method2'不在字典中传递到元类的__new__方法中,所以我们得到KeyError。我喜欢使用元类的想法,因为它允许装饰器操作函数而不是方法,这似乎是一个很好的简化。

1 个答案:

答案 0 :(得分:2)

我假设你真正想做的事情并不是装饰基类的那些方法的副本,而是装饰子类中这些方法的新重写副本。

如果是这样,那很容易。传递给元类的字典中不存在这些方法,因为它们没有在类定义中定义,它们是从基类继承的。哪个元类可以访问。所以:

def __new__(cls, name, bases, d):
    def find_method(m):
        for base in bases:
            try:
                return getattr(base, m)
            except AttributeError:
                pass
        raise AttributeError("No bases have method '{}'".format(m))
    for m in d['methodsToDecorate']:
        d[m] = myDecorator(find_method(m))
    return type(name, bases, d)

如果你确实想要进入基类并替换它的方法,你当然可以使用相同的技巧。只需返回基础以及找到的方法,您就可以setattr


您要求覆盖基类中的方法。但是,如果该方法实际上来自更远的祖先呢?然后这仍然有效...但它可能无法按您想要的方式工作。如果您有多个共享共同祖先的基础,则在每个直接基础上调用getattr将在每个基础上搜索该共同祖先一次,但在新类上调用getattr将仅在结束时搜索一次。

正如user2357112在评论中指出的那样,如果您要覆盖的是“如果我没有覆盖任何东西就会被调用的任何内容”,您可以通过首先构建新类来实现这一点 ,然后通过在结果类本身上调用getattr来修改它。在2.x中,这意味着你必须处理未绑定的方法而不是函数(在3.x中,这不是问题,因为未绑定的方法只是函数),但是好处可能超过了成本。