2012-12-25 79 views
5

Possible Duplicate:
Python lambdas and scoping在Python中定義的函數列表

我希望下將產生的取值0,1和2 3個常數函數的列表:

lis = [] 
for i in range(3): 
    lis.append(lambda: i) 

但他們都最終以價值2.我期望通過深拷貝來解決這個問題,但它似乎不起作用。

+3

這是一個標準的問題。例如http://stackoverflow.com/questions/1107210/python-lambda-problems,http://stackoverflow.com/questions/1924214/python-lambdas-and-scoping。許多,*許多*更多。 –

+0

相關:[爲什麼map()和list的理解結果不一樣?](http://stackoverflow.com/questions/139819/why-results-of-map-and-list-comprehension-are-different) – jfs

回答

1

您環路避免編寫這樣的事:

lis.append(lambda: 0) 
lis.append(lambda: 1) 
lis.append(lambda: 2) 

你的目的是要編寫返回常量整數lambda函數。但是你正在定義和追加一個返回對象i的函數。因此3個附加功能是相同的。

的功能背後的字節代碼創建的回報i

In [22]: import dis 
In [25]: dis.dis(lis[0]) 
    3   0 LOAD_GLOBAL    0 (i) 
       3 RETURN_VALUE   

In [26]: dis.dis(lis[1]) 
    3   0 LOAD_GLOBAL    0 (i) 
       3 RETURN_VALUE   

In [27]: dis.dis(lis[2]) 
    3   0 LOAD_GLOBAL    0 (i) 
       3 RETURN_VALUE 

調用任何這些函數返回的i的最新值2在你的示例代碼:

In [28]: lis[0]() 
Out[28]: 2 

,如果你刪除i對象,您收到錯誤:

In [29]: del i 

In [30]: lis[0]() 
--------------------------------------------------------------------------- 
NameError         Traceback (most recent call last) 
<ipython-input-30-c9e334d64652> in <module>() 
----> 1 lis[0]() 

<ipython-input-18-15df6d11323a> in <lambda>() 
     1 lis = [] 
     2 for i in range(3): 
----> 3  lis.append(lambda: i) 

NameError: global name 'i' is not defined 

一個解決辦法是使用循環來寫出來與大家需要的常量的代碼,以保持和實際運行該代碼:

In [31]: lis = [] 
    ...: for i in range(3): 
    ...:  exec 'lis.append(lambda: {})'.format(i) 
    ...: 

結果如下:

In [44]: lis[0]() 
Out[44]: 0 

In [45]: lis[1]() 
Out[45]: 1 

In [46]: dis.dis(lis[0]) 
    1   0 LOAD_CONST    1 (0) 
       3 RETURN_VALUE   

In [47]: dis.dis(lis[1]) 
    1   0 LOAD_CONST    1 (1) 
       3 RETURN_VALUE   
+1

診斷是正確的(這是一個關閉問題),但請不要使用'exec'!太可怕了!邪惡!討厭!不愉快! –

+0

@ChrisMorgan我這裏唯一的目標是嘗試解釋lambda關閉的另一種方式,因爲它一次又一次地回來......這裏的'exec'是爲了說明期望的源代碼(OP的意圖)的元層次。我同意在@BasicWolf中使用lambda默認參數是解釋之外的方法。 – Boud

+0

@ChrisMorgan這裏唯一的工作解決方案使用exec。你有更好的建議嗎? – kilojoules

3

@ Boud給出了一個非常好的答案,解釋了爲什麼你的代碼不能像你期望的那樣工作。虔誠地說,你必須在lambda中引用它之前評估i的值。這樣做有點怪異:

lis = [] 
for i in range(3): 
    lis.append(lambda i=i: i) 

這使用Python的默認函數參數的值功能,例如,在一個功能的一個會寫:現在

def f(i=10): 
    return i 

,訣竅是一個參數具有存儲在該點它的默認值創建函數(的方法,lambda表達式)時。因此:

j = 10 
def f(i=j): 
    return i 

j = 20 
print(f(125)) # no matter that j has been changed, the output is... 
>>> 125 

而同樣的技巧適用於lambda。爲了使它更清晰:

lis = [] 
for j in range(3): 
    lis.append(lambda i=j: i) 

# Calling the lambdas 
print(lis[1]()) 
>>> 1 
+0

事實上,使用默認參數是最常見的解決方案我見過。 –