Perché i risultati di map () e comprensione delle liste sono diversi?
-
02-07-2019 - |
Domanda
Il seguente test ha esito negativo:
#!/usr/bin/env python
def f(*args):
"""
>>> t = 1, -1
>>> f(*map(lambda i: lambda: i, t))
[1, -1]
>>> f(*(lambda: i for i in t)) # -> [-1, -1]
[1, -1]
>>> f(*[lambda: i for i in t]) # -> [-1, -1]
[1, -1]
"""
alist = [a() for a in args]
print(alist)
if __name__ == '__main__':
import doctest; doctest.testmod()
In altre parole:
>>> t = 1, -1
>>> args = []
>>> for i in t:
... args.append(lambda: i)
...
>>> map(lambda a: a(), args)
[-1, -1]
>>> args = []
>>> for i in t:
... args.append((lambda i: lambda: i)(i))
...
>>> map(lambda a: a(), args)
[1, -1]
>>> args = []
>>> for i in t:
... args.append(lambda i=i: i)
...
>>> map(lambda a: a(), args)
[1, -1]
Soluzione
Sono diversi, perché il valore di i
sia nell'espressione del generatore che nella lista è valutato pigramente, cioè quando le funzioni anonime sono invocate in f
. < br>
A quel punto, i
è associato all'ultimo valore se t
, che è -1.
Quindi, in sostanza, questo è ciò che fa la comprensione dell'elenco (anche per il genexp):
x = []
i = 1 # 1. from t
x.append(lambda: i)
i = -1 # 2. from t
x.append(lambda: i)
Ora i lambda portano una chiusura che fa riferimento a i
, ma i
è associato a -1 in entrambi i casi, poiché è l'ultimo valore a cui è stato assegnato.
Se vuoi assicurarti che lambda riceva il valore corrente di i
, fallo
f(*[lambda u=i: u for i in t])
In questo modo, imponi la valutazione di i
al momento della creazione della chiusura.
Modifica : esiste una differenza tra le espressioni del generatore e le comprensioni dell'elenco: quest'ultima perde la variabile loop nell'ambito circostante.
Altri suggerimenti
Lambda acquisisce variabili, non valori, da cui il codice
lambda : i
restituirà sempre il valore a cui è attualmente associato nella chiusura. Quando viene chiamato, questo valore è stato impostato su -1.
Per ottenere ciò che desideri, dovrai catturare il legame effettivo al momento della creazione della lambda, da:
>>> f(*(lambda i=i: i for i in t)) # -> [-1, -1]
[1, -1]
>>> f(*[lambda i=i: i for i in t]) # -> [-1, -1]
[1, -1]
Espressione f = lambda: i
è equivalente a:
def f():
return i
Espressione g = lambda i = i: i
è equivalente a:
def g(i=i):
return i
i
è una variabile gratuita nella prima case ed è associato al parametro della funzione nel secondo caso, ovvero in questo caso è una variabile locale. I valori per i parametri predefiniti vengono valutati al momento della definizione della funzione.
L'espressione del generatore è l'ambito racchiuso più vicino (dove è definito i
) per i
nell'espressione lambda
, quindi i
è stato risolto in quel blocco:
f(*(lambda: i for i in (1, -1)) # -> [-1, -1]
i
è una variabile locale del blocco lambda i: ...
, pertanto l'oggetto a cui fa riferimento è definito in quel blocco:
f(*map(lambda i: lambda: i, (1,-1))) # -> [1, -1]