問題是tests
中的每個函數都指的是變量i
。
更常見的是,您可以在函數內部執行此操作,在這種情況下,您有一個局部定義範圍的變量i
,它將存儲在閉包中,在These Nasty Closures中有很好的解釋。
但是這裏更簡單:i
是一個全局變量,所以沒有關閉。編譯這些函數以在運行時查找i
作爲全局變量。由於i
已更改,所以函數在運行時將看到更改的值。就那麼簡單。
解決這個問題的傳統方式(這同時適用於封閉和全局變量)被親切地稱爲「默認值黑客」,即使它不是一個真正的黑客。 (見the explanation in the FAQ。)瑞安海寧的回答解釋瞭如何做到這一點:
lambda x, i=i: x%i==0
這將創建一個名爲i
參數,等於在創建函數時的i
值的默認值。然後,在函數內部,當您訪問參數i
時,您將獲得該值。
一種不同的方式解決這個問題,如果您使用的是像JavaScript語言可能看起來比較熟悉,是創建一個函數創建功能,並傳遞的i
值作爲參數傳遞給該功能 - 創建功能,如user2864740的回答是:
(lambda i: lambda x: x%i)(i)
這避免「污染」功能的簽名與一個額外的參數(有人可能會不小心將參數傳遞到),但在創建和調用函數的成本沒有很好的理由。
解決此第三種方法是使用partial
。如果您所要做的只是部分應用功能,則使用partial
而不是定義包裝函數,因爲lambda
可能更清晰。
不幸的是,在這種情況下,函數隱藏在一個運算符中,並且函數operator.mod
公開它並不帶關鍵字參數,所以你不能有用地部分分配它的第二個操作數。所以,在這種情況下這是一個不好的解決方案。如果你真的想,你可以只寫一個表現更好的包裝和partial
是:
def opmod(a, b):
return a % b
partial(operator.mod, b=i)
在這種情況下,我認爲你是與其他解決方案更好;只要保留這個在你的腦袋是適當的情況下。
另請參閱[這些討厭的閉包](http://code.activestate.com/recipes/502271/)比在官方文檔中更好的解釋。但簡短的版本是,這些測試函數中的每一個實際上都是圍繞同一個變量'i'的閉包,而'i'保持不斷變化的值。 (這是不準確的,因爲全局變量實際上並不需要存儲在閉包中,但效果是一樣的。) – abarnert