2011-09-22 16 views
6

Code mówi więcej:Funkcja lambda nie zamyka parametru w Pythonie?


from pprint import pprint 

li = [] 

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

for k in li: 
     k() 

wydajność:

 
4 
4 
4 
4 
4 

dlaczego nie

 
0 
1 
2 
3 
4 

??

Dzięki.

P.S. Jeśli piszę kompletny dekorator, że działa zgodnie z oczekiwaniami:



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

zobacz [to pytanie] (http://stackoverflow.com/q/2295290/195823) i [moja odpowiedź na to pytanie] (http://stackoverflow.com/questions/2295290/what-do-lambda- function-closures-capture-in-python/2295372 # 2295372) –

+0

Możesz zobaczyć, że to zamyka zmienną, przenosząc oryginalną pętlę do funkcji, a następnie wywołując funkcję przed linią 'for k in li:', więc że 'i' nie jest poprawną nazwą w zakresie poziomu modułu. Będzie nadal działać (i uzyskać ten sam wynik, ponieważ zamyka odwołanie, a nie wartość), co oznacza, że ​​nazwa jest zamknięta. – agf

+0

Dziękuję wszystkim.Myślę, że http://stackoverflow.com/questions/2295290/what-do-lambda-function-closures-capture-in-python/2295368#2295368 mówi nie tylko o rozwiązaniu, ale także o tym, dlaczego. Myślę, że moje pytanie jest duplikatem http://stackoverflow.com/questions/2295290/what-do-lambda-function-closures-capture-in-python – Grissiom

Odpowiedz

10

trzeba zrobić:

lambda i=i: pprint(i) 

zamiast uchwycić aktualną wartość i

3

To jest właściwie odwoływać i, tylko Chodzi o to, że do czasu zapełnienia listy, i będzie mieć przypisaną wartość z ostatniego elementu w sekwencji, gdy iteracja się zakończy, dlatego właśnie widzisz wszystko po kolei.

0

Funkcje lambda tworzą zamknięcie w stosunku do zmiennej i. Po zakończeniu pierwszej pętli for, i ma wartość 4.

Następnie rozpoczyna się druga pętla for i wykonywane są wszystkie funkcje lambda. Następnie każdy z nich wypisuje aktualną wartość i, która jest 4.

3

Jeśli nie chcesz korzystać z domyślnego argument - który mógłby przedstawić błędy jeśli przypadkowo wywołany z argumentem - można używać zagnieżdżonych lambda:

from pprint import pprint 

li = [] 

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

for k in li: 
    k() 

To jest anonimowy wersję swojego closure funkcja.

0

Odpowiedź: Ponieważ kod wewnątrz lambda wykorzystuje zmienną globalną i.

Twój drugi wariant będzie tak samo jak pierwszy z lambda jeśli usunąć parametr i:

def closure(): 

Zamiast

def closure(i): 

Tj kod wewnątrz funkcji użyje zmiennej globalnej i.

+0

Nie ma nic wspólnego z zakresem zmiennej, to fakt to, co jest zamknięte, jest odniesieniem do wartości, a nie wartości. Zobacz mój komentarz do pytania - jeśli przeniesiesz pętlę 'for in in range' do funkcji, więc' i' nie istnieje w zasięgu globalnym, a następnie wywołaj funkcję przed pętlą 'for k in li', da tę samą odpowiedź. – agf

+0

> jeśli przesuniesz pętlę zasięgu w funkcji, więc nie istnieje w zasięgu globalnym, a następnie wywołaj funkcję przed pętlą for w pętli li, to da tę samą odpowiedź ... --- ie nie lokalny? O to mi chodziło. – warvariuc

+0

szuka zmiennej "i". ponieważ nie ma argumentu funkcji 'i' pobiera zmienną' i' z zewnętrznego zakresu. – warvariuc