2012-12-25 15 views
5

Possible Duplicate:
Python lambdas and scopingdefiniowania listę funkcji w Pythonie

Mam nadzieję, że następne będzie produkować listę 3 stałych funkcji przyjmujących wartości 0, 1 i 2:

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

Ale wszystkie one kończy się przy wartości 2. Spodziewałbym się, że będzie to poprawka, ale nie wydaje się działać.

+3

To jest standardowe pytanie. na przykład http://stackoverflow.com/questions/1107210/python-lambda-problems, http://stackoverflow.com/questions/1924214/python-lambdas-and-scoping. Wiele, * wiele * więcej. –

+0

powiązane: [Dlaczego wyniki map() i rozumienia list są różne?] (Http://stackoverflow.com/questions/139819/why-results-of-map-and-list-comprehension-are-different) – jfs

Odpowiedz

1

można uniknąć pętli pisząc coś takiego:

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

zamiarem jest napisać lambda funkcje, które zwracają stałej liczby całkowite. Ale definiujesz i dołączasz funkcję zwracającą obiekt i. Zatem 3 dołączone funkcje są takie same.

kodu bajtowego za funkcje stworzone powraca 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 

Wywołanie dowolnej z tych funkcji zwraca ostatnią wartość i który jest 2 w kodzie próbki:

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

jeśli usuniesz i obiekt, pojawia się błąd:

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 

Rozwiązaniem może być, aby utrzymać przy użyciu pętli napisać kod ze stałych trzeba i faktycznie uruchomić ten kod:

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

z następującymi wynikami:

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

diagnoza jest poprawna (jest to problem zamknięcia), ale proszę NIE używaj 'exec'! To straszne! Zło! Paskudny! Nieprzyjemny! –

+0

@ChrisMorgan Moim jedynym celem jest próba wyjaśnienia zamknięć lambda w inny sposób, ponieważ wraca ona i znowu ... 'exec' ma na celu zilustrowanie poziomu meta oczekiwanego kodu źródłowego (zamiar OP). Zgadzam się używając domyślnego arg w lambda, ponieważ @BasicWolf jest sposobem na wyjście poza wyjaśnienia. – Boud

+0

@ChrisMorgan Jedyne działające rozwiązanie wykorzystuje tutaj exec. Czy masz lepszą sugestię? – kilojoules

3

@ Boud dał bardzo dobrą odpowiedź wyjaśniającą, dlaczego twój kod nie działa tak, jak tego oczekujesz. Ba, musisz oszacować wartość i przed odniesieniem w lambda. Oto nieco hacky sposób to zrobić:

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

używa wartości domyślnych argumentów funkcja Pythona wyposażone, np w funkcji należałoby napisać:

def f(i=10): 
    return i 

Teraz sztuką jest, że argument ma swoją wartość domyślną przechowywaną w momencie, gdy funkcja (metoda, wyrażenie lambda) jest tworzony. Tak więc:

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

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

I ta sama sztuczka dotyczy lambda. Aby było nieco jaśniej:

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

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

Rzeczywiście, używanie domyślnych argumentów jest najczęstszym rozwiązaniem, które Widziałem. –

Powiązane problemy