¿Por qué la salida de las siguientes dos comprensiones de lista es diferente, aunque f
la lambda
función sea la misma?
f = lambda x: x*x
[f(x) for x in range(10)]
y
[lambda x: x*x for x in range(10)]
Eso sí, ambos type(f)
y type(lambda x: x*x)
devuelven el mismo tipo.
[lambda x: x*x for x in range(10)]
es más rápido que el primero, ya que no llama a una función de bucle externo, f repetidamente.[x*x for x in range(10)]
es mejor.Respuestas:
El primero crea una sola función lambda y la llama diez veces.
El segundo no llama a la función. Crea 10 funciones lambda diferentes. Pone a todos en una lista. Para que sea equivalente al primero que necesita:
O mejor aún:
fuente
map(lambda x: x*x, range(10))
, que probablemente era lo que el OP quería decir en primer lugar.list(map(lambda x: x*x, range(10)))
te dará[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
Esta pregunta toca una parte muy apestosa de la sintaxis de Python "famosa" y "obvia": lo que tiene prioridad, la lambda o la comprensión de la lista.
No creo que el objetivo del OP sea generar una lista de cuadrados de 0 a 9. Si ese fuera el caso, podríamos dar aún más soluciones:
Pero no es el punto. El punto es W (hy) TF ¿es esta expresión ambigua tan contraintuitiva? Y tengo un caso idiota para ti al final, así que no descartes mi respuesta demasiado pronto (la tuve en una entrevista de trabajo).
Entonces, la comprensión del OP devolvió una lista de lambdas:
Por supuesto, esto es solo 10 copias diferentes de la función de cuadratura, ver:
Tenga en cuenta las direcciones de memoria de las lambdas: ¡todas son diferentes!
Por supuesto, podría tener una versión más "óptima" (jaja) de esta expresión:
¿Ver? 3 veces la misma lambda.
Tenga en cuenta que utilicé
_
comofor
variable. No tiene nada que ver con elx
en ellambda
(se eclipsa léxico!). ¿Consíguelo?Estoy dejando de lado la discusión, por qué la precedencia de sintaxis no es así, que todo significaba:
que podría ser:
[[0, 1, 4, ..., 81]]
o[(0, 1, 4, ..., 81)]
, o lo que me parece más lógico , esto sería unlist
elemento de 1: ungenerator
retorno de los valores. Simplemente no es el caso, el lenguaje no funciona de esta manera.PERO qué, si ...
¿Qué pasa si NO eclipsas el
for
variable Y la usas en tulambda
s ???Bueno, entonces pasa una mierda. Mira este:
esto significa, por supuesto:
PERO NO SIGNIFICA:
¡Esto es una locura!
Las lambdas en la lista de comprensión son un cierre sobre el alcance de esta comprensión. Un cierre léxico , por lo que se refieren a la
i
referencia vía, y no a su valor cuando fueron evaluados!Entonces, esta expresión:
ES aproximadamente EQUIVALENTE a:
Estoy seguro de que podríamos ver más aquí usando un descompilador de Python (con lo que quiero decir, por ejemplo, el
dis
módulo), pero para la discusión independiente de Python-VM esto es suficiente. Esto en cuanto a la pregunta de la entrevista de trabajo.Ahora, ¿cómo hacer un
list
lambdas multiplicador, que realmente se multiplica por enteros consecutivos? Bueno, de manera similar a la respuesta aceptada, necesitamos romper el vínculo directoi
envolviéndolo en otrolambda
, que se llama dentro la expresión de comprensión de la lista:Antes de:
Después:
(También tenía la variable lambda externa =
i
, pero decidí que esta es la solución más clara: presentéy
para que todos podamos ver qué bruja es cuál).Editar 2019-08-30:
Siguiendo una sugerencia de @josoler, que también está presente en una respuesta de @sheridp: el valor de la "variable de bucle" de comprensión de la lista se puede "incrustar" dentro de un objeto; la clave es acceder a él en el momento adecuado. La sección "Después" anterior lo hace envolviéndolo en otro
lambda
y llamándolo inmediatamente con el valor actual dei
. Otra forma (un poco más fácil de leer, ya que no produce ningún efecto 'WAT') es almacenar el valori
dentro de unpartial
objeto y hacer que el "interno" (original) lolambda
tome como argumento (aprobado por elpartial
objeto en el hora de la llamada), es decir:Después de 2:
Genial, ¡pero todavía hay un pequeño giro para ti! Digamos que no queremos facilitar el uso del lector de código y pasar el factor por nombre (como argumento de palabra clave para
partial
). Hagamos un cambio de nombre:Después de 2.5:
WAT?
Espera ... ¿Estamos cambiando el número de argumentos por 1 y pasando de "demasiados" a "muy pocos"?
Bueno, no es un WAT real, cuando pasamos
coef
departial
esta manera, se convierte en un argumento de palabra clave, por lo que debe venir después del posicionalx
argumento , así:Después de 3:
Preferiría la última versión sobre la lambda anidada, pero a cada una su propia ...
fuente
[partial(lambda i, x: i * x, i) for i in (1, 2)]
La gran diferencia es que el primer ejemplo en realidad invoca la lambda
f(x)
, mientras que el segundo ejemplo no.Su primer ejemplo es equivalente a
[(lambda x: x*x)(x) for x in range(10)]
mientras que su segundo ejemplo es equivalente a[f for x in range(10)]
.fuente
El primero
se ejecuta
f()
para cada valor en el rango, por lo que lo hacef(x)
para cada valorel segundo
ejecuta el lambda para cada valor en la lista, por lo que genera todas esas funciones.
fuente
La gente dio buenas respuestas pero olvidó mencionar la parte más importante en mi opinión: en el segundo ejemplo, la
X
comprensión de la lista NO es la misma que laX
de lalambda
función, no tienen ninguna relación. Entonces, el segundo ejemplo es en realidad el mismo que:Las iteraciones internas en
range(10)
solo son responsables de crear 10 funciones lambda similares en una lista (10 funciones separadas pero totalmente similares, devolviendo la potencia 2 de cada entrada).Por otro lado, el primer ejemplo funciona totalmente diferente, porque la X de las iteraciones SI interactúa con los resultados, para cada iteración el valor es,
X*X
por lo tanto, el resultado sería[0,1,4,9,16,25, 36, 49, 64 ,81]
fuente
Las otras respuestas son correctas, pero si está tratando de hacer una lista de funciones, cada una con un parámetro diferente, que se pueda ejecutar más tarde , el siguiente código lo hará:
Si bien el ejemplo está ideado, lo encontré útil cuando quería una lista de funciones en las que cada una imprime algo diferente, es decir
fuente