原因是閉包(lambda或其他)關閉名稱而不是值。當你定義lambda x: test_fun(n, x)
時,n不會被評估,因爲它在函數內部。當該功能被稱爲它評估,此時即有來自循環的最後一個值的值。
你說的要「使用閉,以消除函數簽名變量」的開始,但它並沒有真正工作的方式。 (請參閱下面的內容,這可能會使您滿意,具體取決於「消除」的含義)。函數定義後,函數體內的變量將不會被評估。爲了得到採取,因爲它存在於函數的定義的時間變量的「快照」功能,你必須通過的變量作爲自變量。通常的做法是給函數一個參數,其默認值是來自外部作用域的變量。看這兩個例子之間的區別:
>>> stuff = [lambda x: n+x for n in [1, 2, 3]]
>>> for f in stuff:
... print f(1)
4
4
4
>>> stuff = [lambda x, n=n: n+x for n in [1, 2, 3]]
>>> for f in stuff:
... print f(1)
2
3
4
在第二示例中,傳遞n
爲「鎖定」 n與該函數的當前值的一個參數函數。如果你想以這種方式鎖定值,你必須做這樣的事情。 (如果它不以這種方式工作,像全局變量這樣的東西根本不起作用;在使用時查找自由變量是至關重要的。)
請注意,此行爲沒有任何特定於lambda表達式。如果使用def
來定義引用封閉範圍中的變量的函數,那麼相同的範圍規則將生效。
如果你真的想,你能避免增加額外的參數傳遞給您的返回功能,但這樣做,必須用這個函數在另一個函數,就像這樣:
>>> def makeFunc(n):
... return lambda x: x+n
>>> stuff = [makeFunc(n) for n in [1, 2, 3]]
>>> for f in stuff:
... print f(1)
2
3
4
這裏,內當它被調用時,lambda仍然查找n
的值。但它引用的n
不再是全局變量,而是封閉函數makeFunc
內的局部變量。每調用一次makeFunc
就會創建一個新的局部變量值,並且返回的lambda創建一個閉包,該閉包「保存」用於調用makeFunc
的局部變量值。因此,循環中創建的每個函數都有自己的「私有」變量,稱爲x
。 (對於這個簡單的情況,這也可以使用lambda作爲外部函數--- stuff = [(lambda n: lambda x: x+n)(n) for n in [1, 2, 3]]
---但這是不太可讀的。)
請注意,您仍然必須通過您的n
作爲參數,它是就是這樣,通過這樣做,你不會將它作爲一個參數傳遞給相同的函數,最終進入stuff
列表;而是將它作爲參數傳遞給一個幫助函數,該函數將創建要放入stuff
的函數。使用這種雙函數方法的優點是返回的函數是「乾淨的」並且沒有額外的參數;如果你打包接受很多參數的函數,這可能會很有用,在這種情況下,記住列表中的參數可能會變得混淆。缺點是,以這種方式進行,因爲你需要另一個封閉的函數,所以製作函數的過程更加複雜。結果是存在一個折衷:你可以使函數創建過程更簡單(即不需要兩個嵌套函數),但是你必須使得結果函數更復雜一點(即它有這個額外的n=n
參數)。或者你可以使函數更簡單(即它沒有參數n=
),但是你必須使函數創建過程更加複雜(即,你需要兩個嵌套函數來實現該機制)。
這比對[這些更受歡迎的類似問題的回答]中編寫的任何東西都更好地解釋了這種Python行爲(https://stackoverflow.com/questions/2295290/what-do-lambda-function-closures -capture/23557126) –