2011-08-26 77 views
2

我想知道如果任何人都可以解释,并提供了解决这个问题:提前问题与方法的monkeypatching和引用

$ cat object-override-methods.py 
class A: 
    def foo(self): 
     return 1 

class B: 
    def foo(self): 
     return 1 

for klass in A, B: 
    orig_foo = klass.foo 
    def foo(self): 
     return orig_foo(self) * 2 
    klass.foo = foo 

A().foo() 
B().foo() 
$ python object-override-methods.py 
Traceback (most recent call last): 
    File "object-override-methods.py", line 15, in <module> 
    A().foo() 
    File "object-override-methods.py", line 12, in foo 
    return orig_foo(self) * 2 
TypeError: unbound method foo() must be called with B instance as first argument (got A instance instead) 

感谢。

+0

的解释会有所帮助;只是发布没有上下文的代码并不鼓励人们帮忙。 –

+0

我的回答大部分看起来像RichieHindle的,所以我没有打扰,但对于它的价值,猴子修补==不好 –

+0

理想情况下,API不会在两个不同的类中定义两个相同的方法。有时候糟糕的设计使得设计更加糟糕:) – Ben

回答

2

orig_foo是一个全局变量,每次通过循环都会改变值。循环完成后,orig_foo指的是B.foo

内部函数foo(一次或每次通过循环)在调用它们时都使用全局值orig_foo。所以他们都拨打B.foo(self)

当调用像orig_foo这样的“未绑定方法”时,Python2会检查第一个参数是否为适当类的实例。 A().foo()未通过此检查。 (有趣的是,Python3除去此检查,所以不会有什么类型错误引发,而这种错误可能会变得更难找到。)

为了解决这个问题,必须的orig_foo值绑定到适当的klass。 您可以通过使orig_foofoo的局部变量来完成此操作。一种方法是使orig_foo的参数foo具有默认值。 Python在定义函数时绑定默认值。所以orig_foo=orig_foo结合局部变量orig_fooklass.foo的当前值:

for klass in A, B: 
    orig_foo = klass.foo 
    def foo(self, orig_foo=orig_foo): 
     return orig_foo(self) * 2 
    klass.foo = foo 
+2

使用默认参数的好主意。谢谢。 – Ben

2

因为orig_foo是在全局范围内定义的,所以每次循环时都要践踏它的值。然后,您的新foo方法将共享该践踏的值。

一个简单的解决方法是将代码移动到一个函数,如下所示:

def rebind_foo(klass): 
    orig_foo = klass.foo 
    def foo(self): 
     return orig_foo(self) * 2 
    klass.foo = foo 

for klass in A, B: 
    rebind_foo(klass) 

这确保每个新foo方法得到其自己的orig_foo值。

+1

现在变得非常有意义。在调用方法时正在评估orig_foo,而不是在方法被定义时。非常感谢。 – Ben