2016-03-16 76 views
2

我在python循環中遇到了一些使用lambda函數的奇怪行爲。當我嘗試將lambda函數分配給列表中的字典條目時,以及在函數中使用字典中的其他條目時,只有最後一次循環纔是評估的lambda運算符。所以所有的功能最終都具有相同的價值!Python循環中的lambda運算符

下面是精簡的代碼,它捕獲我正在嘗試的那些行爲奇怪的部分。我的實際代碼更復雜,並不像這樣微不足道,所以我正在尋找一個解釋,最好是一種解決方法。

n=4 
numbers=range(n) 
entries = [dict() for x in numbers] 

for number, entry in zip(numbers,entries): 
    n = number 
    entry["number"] = n 
    entry["number2"] = lambda x: n*1 

for number in numbers: 
    print(entries[number]["number"], entries[number]["number2"](2)) 

輸出是:

0 3 
1 3 
2 3 
3 3 

換句話說,詞典entires這只是整數都很好,並通過循環正確填寫。但lambda函數 - 這是微不足道的,應該只返回與「數字」條目相同的值 - 全部設置爲最後一遍。

發生了什麼事?

回答

1

您可能會遇到將該方法創建爲引用變量n的問題。該函數僅在循環後進行計算,因此您要調用引用n的函數。如果你真行具有在轉讓時評估的功能,你可以把一個函數調用它周圍:

(lambda x: n*1)(2) 

,或者如果你想有使用功能,讓他們引用您想要的特定值。從你的代碼,你可以使用默認參數作爲一種解決方法:

entry["number"] = n 
entry["number2"] = lambda x, n=n: n*1 

的差異歸結爲內存尋址的問題。我想這又是這樣的:

You:  Python, please give me a variable called "n" 
Python: Ok! Here it is, it is at memory slot 1 
You:  Cool! I will now create functions which say take that variable "n" 
     value (at memory slot 1) and multiply it by 1 and return that to me. 
Python: Ok! Got it: 
      1. Take the value at memory slot 1. 
      2. Multiply by 1. 
      3. Return it to you. 
You:  Done with my looping, now evaluate those instructions! 
Python: Ok! Now I will take the value of at memory slot 1 and multiply by 1 
     and give that to you. 
You:  Hey, I wanted each function to reference different values! 
Python: I followed your instructions exactly! 
2

通過您的for循環中,n變量的末尾 - 它不同於靜態語言,如C#,設置爲3,然後被在lambda訪問表達。變量值不固定;因爲網站上的another answer指出,lambda表達式是流暢的,並且將保留對所涉及的變量的引用,而不是在創建時捕獲這些值。 This question也討論你的問題。

爲了解決這個問題,你需要通過默認參數給予lambda表達式新的,局部變量:

entry["number2"] = lambda x, n=n: n*1 

這會在拉姆達的範圍內一個新的變量,叫做N,它設置爲默認值的「外部「的價值。請注意,這是official FAQ認可的解決方案,如Adrien Plisson所述的this answer

現在,您可以像正常一樣調用您的lambda,並忽略可選參數,沒有不良影響。

編輯:正如Sci Prog所述,該解決方案使得n = number成爲冗餘。你的最終代碼將類似於此:

lim = 4 
numbers = range(lim) 
entries = [dict() for x in numbers] 

for number, entry in zip(numbers, entries): 
    entry["number"] = number 
    entry["number2"] = lambda x, n = number: n*1 

for number in numbers: 
    print(entries[number]["number"], entries[number]["number2"](2)) 
+0

很好的答案,雖然我會刪除註釋:「#可選:del lim,除非稍後有必要」。我不認爲有必要在這裏解除來自全局命名空間的'lim'引用。除了一些古怪的情況,'del'通常被認爲是不好的風格,並沒有被使用。對於這樣一個簡單的腳本,將一個單引用整數解除綁定將是非傳統的,不混淆的。 –

+0

不會假裝知道關於高級Python約定的任何事情。我的大部分蟒蛇知識都是作爲一個愛好者。謝謝。 – BHustus

2

試試這個

N=4 
numbers=range(N) 
entries = [dict() for x in numbers] 

for number, entry in zip(numbers,entries): 
    entry["number"] = number 
    entry["number2"] = lambda x,n=number: n*1 

for number in numbers: 
    print(entries[number]["number"], entries[number]["number2"](2)) 

它打印(python3)

0 0 
1 1 
2 2 
3 3 

爲了避免混淆,n在代碼中引用不同的事情。我只在一個地方使用它。

這是一個關閉問題。

+0

在我打字時,您也得到了BHustus的解答。我提出了他的答案。 –

+0

+1表示在此解決方案中仍然使用n的冗餘。介意我是否重做我的答案以反映它? – BHustus

+0

沒問題,繼續。它提高了清晰度。 –