2009-05-08 15 views
5

Perché questo tentativo di creare un elenco di funzioni non funzionanti non funziona?Cosa sta succedendo con l'espressione lambda in questa funzione python?

def p(x, num): 
    print x, num 

def test(): 
    a = [] 
    for i in range(10): 
     a.append(lambda x: p (i, x)) 
    return a 

>>> myList = test() 
>>> test[0]('test') 
9 test 
>>> test[5]('test') 
9 test 
>>> test[9]('test') 
9 test 

Cosa sta succedendo qui?

una funzione che in realtà quello che mi aspetto che la funzione di cui sopra da fare è:

import functools 
def test2(): 
    a = [] 
    for i in range (10): 
     a.append(functools.partial(p, i)) 
    return a 


>>> a[0]('test') 
0 test 
>>> a[5]('test') 
5 test 
>>> a[9]('test') 
9 test 
+0

Poiché si dispone di una soluzione che utilizza functools.partial, qual è la domanda? –

+2

La domanda è: perché il primo metodo non funziona? – David

risposta

12

In Python, le variabili create in loop e filiali non sono scope. Tutte le funzioni che stai creando con lambda hanno un riferimento alla stessa variabile i, che è impostata su 9 sull'ultima iterazione del ciclo.

La soluzione è creare una funzione che restituisce una funzione, quindi scopre la variabile iteratore. Questo è il motivo per cui l'approccio functools.partial() funziona. Ad esempio:

+0

Capito. Grazie mille! – David

1

Ebbene si può anche associare il i per un lambda esterna per i più pigri.

def p(x, num): 
    print x, num 

def test(): 
    a = [] 
    for i in range(10): 
     a.append((lambda i :lambda x: p (i, x))(i)) 
    return a 
1

Sono sempre stato confuso sul perché questo non funziona. Grazie per la spiegazione, 'a paid nerd'. Io personalmente preferisco questa soluzione:

for i in range(10): 
    a.append(lambda num, val_i=i: p (val_i, num)) 

Nota l'argomento val_i=i default del lambda che consente di catturare il valore istantaneo della i durante il ciclo mentre ancora fatto rendendo lambda una funzione di 1 variabile. (BTW:. Cambiato il tuo x in num per abbinare p 's definizione) Mi piace di più perché:

  1. è molto vicino all'idea originale e evita di dover definire una nuova funzione denominata, appunto lo scopo di un lambda ...
  2. evita l'importazione functools
  3. ed evita lambda imbricating ...

appena fatto una ricerca e ho trovato spiegazioni più dettagliate per lo stesso problema c'è: Scope of python lambda functions and their parameters

+0

L'argomento con nome è il motivo per cui funziona e perché il tuo originale no. Ecco la mia esplorazione delle chiusure in Python: https://gist.github.com/maxcountryman/5035489 – maxcountryman

Problemi correlati