使用MethodType将方法从实例动态添加到另一个实例

时间:2015-04-24 07:36:09

标签: python python-3.x

我试图用新方法装饰类实例。但是如果装饰者从它自己的方法中添加了一个方法,它会给我一个错误

TypeError: myMethod() takes 1 positional argument but 2 were given

举一个最小的例子,假设我有一个MyElement类我无法修改,而且Decorator将一个方法从它自己添加到MyElement的实例

class MyElement(object):
    def __init(self):
        self._name = "MyElement"

class Decorator(object):

    def myMethod(self):
        print(self._name)

    def decorate(self, element):
        element.myMethod = MethodType(self.myMethod, element)

if __name__ == '__main__':
    d = Decorator()
    p = MyElement()

    d.decorate(p)
    p.myMethod()

这给了我上面的错误。 Altough如果我改变装饰,它可以工作:

def decorate(self, element): 
    element.myMethod = MethodType(self.myMethod.__func__, element)

有人可以解释一下MethodType究竟在做什么吗?为什么 func 标志是必要的?

1 个答案:

答案 0 :(得分:2)

您正在使用MethodTypeDecorator myMethod方法绑定到另一个对象。这不起作用,因为当您使用self.myMethod访问它时,您已经获得了绑定方法。第一个绑定将Decorator对象作为self传递,第二个绑定在尝试将MyElement实例作为第二个参数传递时导致异常。

有几种方法可以解决这个问题。目前尚不清楚哪一个最适合您,因为myMethod示例并未对self执行任何操作。

一种选择是通过myMethod课程访问Decorator,而不是通过self。这意味着它将被解除绑定(直到您将其包装在MethodType中)。在此版本中,self中显示的myMethod值将是MyElement实例,而不是Decorator(这对于阅读代码的人来说可能会令人惊讶)。在Python 2中,未绑定的方法进行了特殊检查self是正确的类型,因此这不起作用。

def decorate(self, element):
    element.myMethod = MethodType(Decorator.myMethod, element)

另一种选择是将self.myMethod的绑定方法保存为element上的变量而不尝试再绑定它。使用这种方法,self仍将是装饰器对象,即使该方法是通过MyElement对象调用的。

def decorate(self, element):
    element.myMethod = self.myMethod

最后一个选项是保留decorate方法,但为myMethod添加一个额外的参数。这将允许传递Decorator实例(作为self)和MyElement实例:

def myMethod(self, element):
    print("{} is decorated by {}".format(element, self))