Esta pregunta toca un muy Apestosa parte de la sintaxis de Python "famosa" y "obvia": lo que tiene prioridad, la lambda o la lista de comprensión.
No creo que el propósito del OP fuera generar una lista de cuadrados del 0 al 9.Si ese fuera el caso, se podría dar aún más soluciones:
squares = []
for x in range(10): squares.append(x*x)
- Esta es la forma good ol' de la sintaxis imperativa.
Pero no es el punto. El punto es W (hy) TF es esta expresión ambigua tan contraintuitiva? Y tengo un caso estúpido para usted al final, así que no descarte mi respuesta demasiado pronto (la tuve en una entrevista de trabajo).
Por lo tanto, la comprensión de la OP devuelve una lista de lambdas:
[(lambda x: x*x) for x in range(10)]
Esto es por supuesto sólo 10 diferentes copias de la función cuadrada, véase:
>>> [lambda x: x*x for _ in range(3)]
[<function <lambda> at 0x00000000023AD438>, <function <lambda> at 0x00000000023AD4A8>, <function <lambda> at 0x00000000023AD3C8>]
Nota la direcciones de memoria de las lambdas, ¡todas son diferentes!
Se podría, por supuesto, haya un (jaja) versión más "óptima" de esta expresión:
>>> [lambda x: x*x] * 3
[<function <lambda> at 0x00000000023AD2E8>, <function <lambda> at 0x00000000023AD2E8>, <function <lambda> at 0x00000000023AD2E8>]
Ver? 3 veces el mismo lambda.
Tenga en cuenta que utilicé _
como la variable for
. No tiene nada que ver con el x
en el lambda
(se eclipsó léxicamente!). ¿Consíguelo?
estoy dejando de lado la discusión, ¿por qué la precedencia sintaxis no es así, que significaba todo aquello:
[lambda x: (x*x for x in range(10))]
que podría ser: [[0, 1, 4, ..., 81]]
, o [(0, 1, 4, ..., 81)]
o que me parece más lógico, esto sería un list
de 1 elemento - un generator
devolviendo los valores. Simplemente no es el caso, el lenguaje no funciona de esta manera.
PERO Qué, si ...
¿Qué pasa si usted NO opacar la variable for
, y utilizarlo en sus lambda
s ???
Bueno, entonces sucede una mierda. Mira esto:
[lambda x: x * i for i in range(4)]
esto significa, por supuesto:
[(lambda x: x * i) for i in range(4)]
pero no lo hace significa:
[(lambda x: x * 0), (lambda x: x * 1), ... (lambda x: x * 3)]
Esto es una locura!
Las lambdas en la lista de comprensión son un cierre sobre el alcance de esta comprensión. A léxico cierre, por lo que se refieren al i
a través de referencia, ¡y no su valor cuando se evaluaron!
Así, esta expresión:
[(lambda x: x * i) for i in range(4)]
es aproximadamente equivalente a:
[(lambda x: x * 3), (lambda x: x * 3), ... (lambda x: x * 3)]
Estoy seguro de que podríamos ver más aquí usando un decompilador pitón (y me refiero, por ejemplo, el módulo dis
), pero para la discusión de Python-VM-agnostic esto es suficiente. Tanto para la pregunta de la entrevista de trabajo.
Ahora, ¿cómo hacer un list
de multiplicador lambdas, que realmente se multiplica por enteros consecutivos? Pues bien, de manera similar a la respuesta aceptada, que necesitamos para romper el empate directo a i
envolviéndolo en otro lambda
, que está recibiendo llamadas dentro la expresión lista por comprensión:
Antes:
>>> a = [(lambda x: x * i) for i in (1, 2)]
>>> a[1](1)
2
>>> a[0](1)
2
Después :
>>> a = [(lambda y: (lambda x: y * x))(i) for i in (1, 2)]
>>> a[1](1)
2
>>> a[0](1)
1
(que tenía la variable lambda exterior también = i
, pero decidí esta es la solución más clara - introduje y
para que todos podamos ver qué bruja es cuál).
'[lambda x: x * x para x en el rango (10)]' es más rápido que el primero, ya que no llama a una función de bucle externo, f rep con entusiasmo – riza
@Selinap: ... no, en su lugar está creando una nueva función de marca cada vez que pasa el ciclo. ... y la sobrecarga de crear esta nueva función, luego llamar es un poco más lento (en mi sistema de todos modos). – Gerrat
@Gerrat: incluso con gastos generales, es aún más rápido. Pero, por supuesto, '[x * x para x en el rango (10)]' es mejor. – riza