2012-11-13 42 views
24

我試圖使用閉包,以消除從一個函數簽名的變量(該應用程序是使寫入所需的一個接口來控制的參數一個相當大的數量的字典連接Qt的信號的所有的功能即存儲值)。Python的拉姆達閉合作用域

我不明白爲什麼使用lambda沒有包裹在其他功能的情況下,對所有的情況下返回姓氏。

names = ['a', 'b', 'c'] 

def test_fun(name, x): 
    print(name, x) 

def gen_clousure(name): 
    return lambda x: test_fun(name, x) 

funcs1 = [gen_clousure(n) for n in names] 
funcs2 = [lambda x: test_fun(n, x) for n in names] 

# this is what I want 
In [88]: for f in funcs1: 
    ....:  f(1) 
a 1 
b 1 
c 1 

# I do not understand why I get this 
In [89]: for f in funcs2: 
    ....:  f(1) 
c 1 
c 1 
c 1 

回答

44

原因是閉包(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=),但是你必須使函數創建過程更加複雜(即,你需要兩個嵌套函數來實現該機制)。

+0

這比對[這些更受歡迎的類似問題的回答]中編寫的任何東西都更好地解釋了這種Python行爲(https://stackoverflow.com/questions/2295290/what-do-lambda-function-closures -capture/23557126) –