2011-09-22 30 views
6

代碼會談更多:lambda函數不要在Python中關閉參數?


from pprint import pprint 

li = [] 

for i in range(5): 
     li.append(lambda : pprint(i)) 

for k in li: 
     k() 

產量:

 
4 
4 
4 
4 
4 

爲什麼不

 
0 
1 
2 
3 
4 

謝謝。

P.S.如果我寫了完整的裝飾,它按預期工作:



from pprint import pprint 

li = [] 

#for i in range(5): 
     #li.append(lambda : pprint(i)) 

def closure(i): 
     def _func(): 
       pprint(i) 
     return _func 

for i in range(5): 
     li.append(closure(i)) 

for k in li: 
     k() 
+0

請參閱[此問題](http://stackoverflow.com/q/2295290/195823)和[我對此問題的回答](http://stackoverflow.com/questions/2295290/what-do-lambda-函數閉包捕獲在python/2295372#2295372) –

+0

你可以看到,通過將原始循環移動到一個函數,然後在'for k in li:'行之前調用函數來關閉變量,所以'i'不是模塊級範圍中的有效名稱。它仍然可以工作(並且得到相同的結果,因爲它關閉了一個引用而不是值),這意味着該名稱已關閉。 – agf

+0

謝謝大家。我認爲http://stackoverflow.com/questions/2295290/what-do-lambda-function-closures-capture-in-python/2295368#2295368不僅告訴解決方案,但也是爲什麼。我認爲我的問題是重複與http://stackoverflow.com/questions/2295290/what-do-lambda-function-closures-capture-in-python – Grissiom

回答

10

你需要做的:

lambda i=i: pprint(i) 

,而不是捕捉到的i

3

當前值它不恰當地引用i,只問題是,當你的列表被填充時,當迭代結束時,i將具有從序列中最後一項分配的值,所以這就是爲什麼你看到4全部結束。

0

lambda函數通過i變量創建閉包。在第一個for循環完成後,i的值爲4

然後第二個for循環啓動並執行所有lambda函數。他們中的每一個然後打印當前值i,這是4

3

如果你不想使用默認參數 - 這可能引入錯誤,如果有一個參數叫做意外 - 你可以使用一個嵌套的拉姆達:

from pprint import pprint 

li = [] 

for i in range(5): 
    li.append((lambda x: lambda: pprint(x))(i)) 

for k in li: 
    k() 

這是一個匿名版本的closure功能。

0

答案:因爲lambda中的代碼正在使用您的全局變量i

你的第二個變種會做同樣的作爲第一個與拉姆達如果刪除了參數i

def closure(): 

而不是

def closure(i): 

即函數內的代碼將使用全局變量i

+0

它與變量的範圍無關,這是事實所關閉的是對價值而不是價值的引用。看到我對這個問題的評論 - 如果你將'for in in range'循環移動到一個函數中,所以'i'不存在於全局作用域中,然後在'for k in li'循環之前調用該函數,它會給出相同的答案。 – agf

+0

>如果你將範圍循環中的i移動到一個函數中,所以我不存在於全局範圍中,然後在for循環中的k之前調用函數,它會給出相同的答案...... --- ie非本地?我正是這個意思。 – warvariuc

+0

它正在尋找變量'i'。因爲沒有函數參數'i',它需要從外部範圍變量'i'。 – warvariuc