使用静态装饰器进行动态装饰

时间:2017-02-02 06:06:02

标签: python inheritance decorator

所以我想更新一个库中的类,但我无法控制该类(我无法触及原始源代码)。约束数字2:其他用户已经继承了这个父类,并要求他们从第三个类继承将有点“烦人”。所以我必须同时使用这两个约束:需要扩展父类,而不是通过继承它。

一个解决方案起初似乎更有意义,尽管它接近于“猴子修补”。我自己覆盖父类的一些方法。我写了一个小装饰师,可以做到这一点。但是我遇到了一些错误,而不是给你整个代码,这里有一个例子。考虑下面这个名为Old here(父类)的类在我无法触及的库中(关于它的源代码,无论如何):

class Old(object):

    def __init__(self, value):
        self.value = value

    def at_disp(self, other):
        print "Value is", self.value, other
        return self.value

这是一个简单的类,一个构造函数和一个带参数的方法(以测试更多)。到目前为止,真的很难。但是我的装饰者来扩展这个类的方法:

def override_hook(typeclass, method_name):
    hook = getattr(typeclass, method_name)
    def wrapper(method):
        def overriden_hook(*args, **kwargs):
            print "Before the hook is called."
            kwargs["hook"] = hook
            ret = method(*args, **kwargs)
            print "After the hook"
            return ret
        setattr(typeclass, method_name, overriden_hook)
        return overriden_hook
    return wrapper

@override_hook(Old, "at_disp")
def new_disp(self, other, hook):
    print "In the new hook, before"
    ret = hook(self, other)
    print "In the new hook, after"
    return ret

令人惊讶的是,这完美无缺。如果您创建一个Old实例,并调用其at_disp方法,则调用新方法(并调用旧方法)。很像隐藏的遗产。但这是真正的挑战:

我们将尝试从Old继承一个类。这就是用户所做的。我的“补丁”也应该适用于他们,而不需要他们做任何事情:

class New(Old):
    def at_disp(self, other):
        print "That's in New..."
        return super(Old, self).at_disp(self, other)

如果您创建一个New对象,并尝试使用其at_disp方法......它会崩溃。 super()在Old中找不到at_disp。这很奇怪,因为New直接继承了Old。我的猜测是,因为我的新替换方法是未绑定的,所以super()找不到它。如果通过直接调用Old.at_disp()替换super(),一切正常。

有人知道如何解决这个问题吗?为什么会发生?

非常感谢!

1 个答案:

答案 0 :(得分:1)

两个问题。

首先,对super的调用应该是super(New, self),而不是super(Old, self)super的第一个参数通常是“当前”类(即方法调用super的类)。

其次,对at_disp方法的调用应该是at_disp(other),而不是at_disp(self, other)。当你使用super的双参数形式时,你会得到一个像实例一样的绑定超级对象,所以如果你在它上面调用一个方法,self会自动传递。

所以电话应该是super(New, self).at_disp(other)。然后就行了。