2014-01-10 132 views
14

我想玩匿名函數,所以我決定做一個簡單的主要發現者。那就是:lambda函數訪問外部變量

tests = [] 
end = int(1e2) 
i = 3 
while i <= end: 
    a = map(lambda f:f(i),tests) 
    if True not in a: 
     tests.append(lambda x:x%i==0) 
     print i 
    print tests 
    print "Test: "+str(i) 
    print str(a) 
    i+=2 

什麼,但是我發現,是在lambda x:x%i==0i每次訪問,而我希望它是一個字面上的數字。我怎樣才能讓它變成lambda x:x%3==0呢?

+1

另請參閱[這些討厭的閉包](http://code.activestate.com/recipes/502271/)比在官方文檔中更好的解釋。但簡短的版本是,這些測試函數中的每一個實際上都是圍繞同一個變量'i'的閉包,而'i'保持不斷變化的值。 (這是不準確的,因爲全局變量實際上並不需要存儲在閉包中,但效果是一樣的。) – abarnert

回答

27

可以「捕獲」 i創建時的拉姆達

lambda x, i=i: x%i==0 

這將設置i在拉姆達的情況下等於任何它被創建時i了。如果你願意,你也可以說lambda x, n=i: x%n==0,它不完全捕獲,但它會得到你所需要的。

如果沒有這一點,因爲你所看到的,它要尋找一個i在封閉範圍


這是查找的問題,這是類似與定義的功能如下:

i = "original" 

def print_i1(): 
    print(i) # prints "changed" when called below 

def print_i2(s=i): #default set at function creation, not call 
    print(s) # prints "original" when called below 


i = "changed" 
print_i1() 
print_i2() 
+0

哦,聰明。 – user2864740

+0

+1非常好:) –

+2

這是可行的,因爲當函數被創建*時,默認參數被評估,而當函數被*調用*時,變量查找被完成。 –

2

創建一個返回lambda的新函數。然後調用它,傳遞i作爲參數。這將創建一個新的綁定範圍。

def make_test (i): 
    # this i refers to the parameter (which evaluates to the /value/ passed) 
    return lambda x: x%i==0 

# .. 
# the /value/ resulting from evaluating the variable is passed 
tests.append(make_test(i)) 
6

問題是tests中的每個函數都指的是變量i

更常見的是,您可以在函數內部執行此操作,在這種情況下,您有一個局部定義範圍的變量i,它將存儲在閉包中,在These Nasty Closures中有很好的解釋。

但是這裏更簡單:i是一個全局變量,所以沒有關閉。編譯這些函數以在運行時查找i作爲全局變量。由於i已更改,所以函數在運行時將看到更改的值。就那麼簡單。


解決這個問題的傳統方式(這同時適用於封閉和全局變量)被親切地稱爲「默認值黑客」,即使它不是一個真正的黑客。 (見the explanation in the FAQ。)瑞安海寧的回答解釋瞭如何做到這一點:

lambda x, i=i: x%i==0 

這將創建一個名爲i參數,等於在創建函數時的i值的默認值。然後,在函數內部,當您訪問參數i時,您將獲得該值。


一種不同的方式解決這個問題,如果您使用的是像JavaScript語言可能看起來比較熟悉,是創建一個函數創建功能,並傳遞的i值作爲參數傳遞給該功能 - 創建功能,如user2864740的回答是:

(lambda i: lambda x: x%i)(i) 

這避免「污染」功能的簽名與一個額外的參數(有人可能會不小心將參數傳遞到),但在創建和調用函數的成本沒有很好的理由。


解決此第三種方法是使用partial。如果您所要做的只是部分應用功能,則使用partial而不是定義包裝函數,因爲lambda可能更清晰。

不幸的是,在這種情況下,函數隱藏在一個運算符中,並且函數operator.mod公開它並不帶關鍵字參數,所以你不能有用地部分分配它的第二個操作數。所以,在這種情況下這是一個不好的解決方案。如果你真的想,你可以只寫一個表現更好的包裝和partial是:

def opmod(a, b): 
    return a % b 

partial(operator.mod, b=i) 

在這種情況下,我認爲你是與其他解決方案更好;只要保留這個在你的腦袋適當的情況下。

相關問題