2010-12-13 12 views
5

Ho il seguente codice Python che genera un elenco di funzioni anonime:Generazione di un elenco di funzioni in python

basis = [ (lambda x: n*x) for n in [0, 1, 2] ]  
print basis[0](1) 

mi sarei aspettato che fosse equivalente a

basis = [ (lambda x: 0*x), (lambda x: 1*x), (lambda x: 2*x) ] 
print basis[0](1) 

Tuttavia, mentre il secondo frammento stampa 0 che è quello che mi aspetterei, le prime stampe 2. Cosa c'è di sbagliato nel primo frammento di codice e perché non si comporta come previsto?

+0

domanda relativa: http://stackoverflow.com/q/139819/4279 – jfs

risposta

8

È possibile utilizzare un parametro di default per creare una chiusura su n

>>> basis = [ (lambda x,n=n: n*x) for n in [0, 1, 2] ]  
>>> print basis[0](1) 
0 
+3

Questa tecnica viene spesso utilizzata per passare metodi come funzioni di callback. Assomiglia a 'register_event_handler (event = evt, callback = lambda cbVar1, cbVar2, s = self: s.handle_evt (cbVar1, cbVar2))'. –

2

Il problema è che nel primo esempio, ogni lambda è associata allo stesso n - in altre parole, sta acquisendo la variabile, non il valore della variabile. Poiché n ha il valore 2 alla fine del loop, ogni lambda utilizza il valore 2 per n.

A quanto pare è possibile utilizzare i parametri di default per risolvere questo problema:

basis = [ (lambda x,n=n: n*x) for n in [0, 1, 2] ] 
print basis[0](1) 

Poiché i valori dei parametri di default sono costanti, il n sul lato destro della n=n saranno valutate di volta in volta attraverso il ciclo di darvi una nuova valore catturato.

+0

Qualche suggerimento su come risolverlo? – dzhelil

+1

celil: ho modificato la mia risposta per offrire una soluzione suggerita, ma non è molto elegante. – Gabe

+0

I parametri predefiniti potrebbero non essere eleganti, ma sono molto più facili da vedere rispetto alla valutazione parziale esplicita come ho fatto io: 'basis = [(lambda n: (lambda x: n * x)) (n) per n nell'intervallo (3)] '. :/Il parametro predefinito non è una "costante" nel senso di essere immutabile, ma viene valutato e associato al '.func_defaults' di lambda. Anche i parametri di chiusura lo fanno, ma apparentemente in un modo più magico ... –

3

Perché è "pass by name".

Cioè, quando il lambda viene eseguito, esegue n*x: x è destinata a 1 (si tratta di un parametro), n viene cercato nell'ambiente (è ora 2). Quindi, il risultato è 2.

+0

Questo non spiega perché lo stesso comportamento è esibito quando usiamo invece una comprensione del generatore, che non "perde" n "dentro" locals() '. AFAICT, il valore di 'n' è cercato da' .func_closure' di lambda, e non è affatto chiaro come questo si riferisca magicamente a qualsiasi cosa l'ultima volta che nè si è riferita a quando 'n' non esiste più. –

0

voglio aiutare con la comprensione del commento Karl Knechtel (13 dicembre '10 alle 7 : 32). Il codice seguente mostra come utilizzare il generatore, la definizione lambda originale dà il risultato previsto, ma non con lista o tupla:

>>> #GENERATOR 
... basis = ((lambda x: n*x) for n in [0, 1, 2]) 
>>> print(type(basis)) 
<type 'generator'> 
>>> basis = ((lambda x: n*x) for n in [0, 1, 2]) 
>>> print([x(3) for x in basis]) 
[0, 3, 6] 
>>> #TUPLE 
... basis = tuple((lambda x: n*x) for n in [0, 1, 2]) 
>>> print(type(basis)) 
<type 'tuple'> 
>>> print([x(3) for x in basis]) 
[6, 6, 6] 
>>> #LIST 
... basis = list((lambda x: n*x) for n in [0, 1, 2]) 
>>> print(type(basis)) 
<type 'list'> 
>>> print([x(3) for x in basis]) 
[6, 6, 6] 
>>> #CORRECTED LIST 
... basis = list((lambda x, n=n: n*x) for n in [0, 1, 2]) 
>>> print(type(basis)) 
<type 'list'> 
>>> print([x(3) for x in basis]) 
[0, 3, 6] 
Problemi correlati