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

Posted

技术标签:

【中文标题】如何将装饰器应用于超类的方法?【英文标题】:How do I apply decorators to a superclass's methods? 【发布时间】:2014-03-20 16:08:23 【问题描述】:

假设我有一个 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。我喜欢使用元类的想法,因为它允许装饰器操作函数,而不是方法,这似乎是一个很好的简化。

【问题讨论】:

我不认为你真的想装饰基类方法只是因为创建了一些子类,是吗?这会影响基类的每个实例,以及它的所有独立子类,这会导致代码非常混乱。您是否只想用超类方法的修饰版本覆盖子类中的方法?因为这要简单得多,也更明智。 附带说明,您使用的是 Python 2 风格的元类,但 Python 3 风格的 print 方法。您实际使用的是哪种语言? 我使用的是 python 2.7,但习惯于使用 Python 3.x 打印语句,因为它们都适用。 好的,很酷。只是想检查一下。我见过有人问“为什么我的元类不起作用”,因为他们试图在 3.x 中使用 __metaclass__... 【参考方案1】:

我假设您真正想要做的不是装饰基类的这些方法的副本,而是装饰子类中这些方法的新覆盖副本。

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

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)

如果你真的想进入基类并替换它的方法,你当然可以使用相同的技巧来做到这一点。只需将 base 与找到的方法一起返回,这样您就可以setattr 它。


您要求重写基类中的方法。但是,如果该方法实际上来自更远的祖先呢?那么这仍然可以工作......但它可能无法按照您想要的方式工作。如果您有多个共享共同祖先的基础,则在每个直接基础上调用 getattr 将在每个基础上搜索该共同祖先一次,但在新类上调用 getattr 只会在最后搜索一次。

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

【讨论】:

创建类可能会更好,然后装饰方法以便正确遵循 MRO。 @user2357112:同意,但这似乎是对他正在尝试做的事情的单独改进,而不是他所要求的,所以……也许最好离开它在评论中而不是在答案中?你怎么看? @abarnert:完美。另外,感谢您理解我的意思而不是我所说的:) @user2357112:再想一想,我认为它可能确实属于答案。不管怎样,谢谢你提出来。 @abarnert:我认为对于 python 2.x,您应该将 myDecorator(find_method(m)) 更改为 myDecorator(find_method(m).im_func)。那有意义吗?我认为这解决了未绑定方法与函数的问题。

以上是关于如何将装饰器应用于超类的方法?的主要内容,如果未能解决你的问题,请参考以下文章

装饰器、装饰器类与类装饰器(三)

如何使用装饰器将类的方法添加到类内的列表中

装饰类的继承类方法中的 cls 行为

基于 Python 类的装饰器,带有可以装饰方法或函数的参数

如何使用给定的装饰器获取python类的所有方法

如何在基类的函数上使用装饰器并在继承基类时让装饰器在该函数上工作