Questa domanda tocca una parte molto puzzolente della sintassi "famosa" e "ovvia" di Python - ciò che ha la precedenza, la lambda o la comprensione della lista.
Non penso che lo scopo del PO fosse quello di generare un elenco di quadrati da 0 a 9. In tal caso, potremmo dare ancora più soluzioni:
squares = []
for x in range(10): squares.append(x*x)
- questo è il buon vecchio modo di sintassi imperativa.
Ma non è questo il punto. Il punto è che W (hy) TF è questa espressione ambigua così controintuitiva? E alla fine ho un caso idiota per te, quindi non respingere la mia risposta troppo presto (l'ho fatta durante un colloquio di lavoro).
Pertanto, la comprensione del PO ha restituito un elenco di lambda:
[(lambda x: x*x) for x in range(10)]
Questo è ovviamente solo 10 copie diverse della funzione di quadratura, vedi:
>>> [lambda x: x*x for _ in range(3)]
[<function <lambda> at 0x00000000023AD438>, <function <lambda> at 0x00000000023AD4A8>, <function <lambda> at 0x00000000023AD3C8>]
Nota gli indirizzi di memoria dei lambda: sono tutti diversi!
Ovviamente potresti avere una versione più "ottimale" (haha) di questa espressione:
>>> [lambda x: x*x] * 3
[<function <lambda> at 0x00000000023AD2E8>, <function <lambda> at 0x00000000023AD2E8>, <function <lambda> at 0x00000000023AD2E8>]
Vedere? 3 volte lo stesso lambda.
Si noti che ho usato _come forvariabile. Non ha nulla a che fare con il xin lambda(è oscurato in modo lessicale!). Prendilo?
Lascio fuori la discussione, perché la precedenza della sintassi non è così, che significava tutto:
[lambda x: (x*x for x in range(10))]
quale potrebbe essere:, [[0, 1, 4, ..., 81]]o [(0, 1, 4, ..., 81)], o che trovo più logico , questo sarebbe un listelemento di 1 - a generatorrestituire i valori. Non è proprio il caso, la lingua non funziona in questo modo.
MA cosa, se ...
Che cosa succede se NON offuschi la forvariabile e la usi nella tua lambdas ???
Bene, allora succede una schifezza. Guarda questo:
[lambda x: x * i for i in range(4)]
questo significa ovviamente:
[(lambda x: x * i) for i in range(4)]
MA NON significa:
[(lambda x: x * 0), (lambda x: x * 1), ... (lambda x: x * 3)]
Questo è semplicemente pazzo!
I lambda nella comprensione dell'elenco sono una chiusura del campo di applicazione di questa comprensione. Una chiusura lessicale , quindi si riferiscono al iriferimento via e non al suo valore quando sono stati valutati!
Quindi, questa espressione:
[(lambda x: x * i) for i in range(4)]
È approssimativamente equivalente a:
[(lambda x: x * 3), (lambda x: x * 3), ... (lambda x: x * 3)]
Sono sicuro che potremmo vedere di più qui usando un decompilatore python (con il quale intendo ad esempio il dismodulo), ma per la discussione agnostica su Python-VM questo è sufficiente. Questo per quanto riguarda la domanda del colloquio di lavoro.
Ora, come creare un listmoltiplicatore lambda, che si moltiplica davvero per numeri interi consecutivi? Bene, analogamente alla risposta accettata, dobbiamo spezzare il legame diretto iavvolgendolo in un altro lambda, che viene chiamato all'interno dell'espressione di comprensione dell'elenco:
Prima:
>>> a = [(lambda x: x * i) for i in (1, 2)]
>>> a[1](1)
2
>>> a[0](1)
2
Dopo:
>>> a = [(lambda y: (lambda x: y * x))(i) for i in (1, 2)]
>>> a[1](1)
2
>>> a[0](1)
1
(Avevo anche la variabile lambda esterna = i, ma ho deciso che questa è la soluzione più chiara - ho introdotto in ymodo che tutti possiamo vedere quale strega è quale).
Modifica 30-08-2019:
Seguendo un suggerimento di @josoler, che è anche presente in una risposta di @sheridp - il valore della "variabile di loop" di comprensione dell'elenco può essere "incorporato" all'interno di un oggetto - la chiave è che sia possibile accedervi al momento giusto. La sezione "Dopo" sopra lo fa avvolgendolo in un altro lambdae chiamandolo immediatamente con il valore corrente di i. Un altro modo (un po 'più semplice da leggere - non produce alcun effetto "WAT") è quello di memorizzare il valore iall'interno di un partialoggetto e far sì che "interno" (originale) lo lambdaprenda come argomento (passato fornito partialdall'oggetto al ora della chiamata), ovvero:
Dopo 2:
>>> from functools import partial
>>> a = [partial(lambda y, x: y * x, i) for i in (1, 2)]
>>> a[0](2), a[1](2)
(2, 4)
Fantastico, ma c'è ancora una piccola svolta per te! Supponiamo che non vogliamo semplificare il lettore di codice e passare il fattore per nome (come argomento di parola chiave a partial). Facciamo un po 'di rinomina:
Dopo 2.5:
>>> a = [partial(lambda coef, x: coef * x, coef=i) for i in (1, 2)]
>>> a[0](1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: <lambda>() got multiple values for argument 'coef'
WAT?
>>> a[0]()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: <lambda>() missing 1 required positional argument: 'x'
Aspetta ... Stiamo cambiando il numero di argomenti di 1 e passando da "troppi" a "troppo pochi"?
Beh, non è un vero e proprio WAT, quando si passa coefa partialin questo modo, diventa un argomento parola chiave, quindi deve venire dopo la posizionale xargomento, in questo modo:
Dopo 3:
>>> a = [partial(lambda x, coef: coef * x, coef=i) for i in (1, 2)]
>>> a[0](2), a[1](2)
(2, 4)
Preferirei l'ultima versione al lambda nidificato, ma a ciascuno il suo ...
[lambda x: x*x for x in range(10)]è più veloce del primo, poiché non chiama ripetutamente una funzione di loop esterno.