2011-08-26 55 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