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:
squares = []
for x in range(10): squares.append(x*x)
- Esta es la buena forma de sintaxis imperativa.
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:
[(lambda x: x*x) for x in range(10)]
Por supuesto, esto es solo 10 copias diferentes de la función de cuadratura, ver:
>>> [lambda x: x*x for _ in range(3)]
[<function <lambda> at 0x00000000023AD438>, <function <lambda> at 0x00000000023AD4A8>, <function <lambda> at 0x00000000023AD3C8>]
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:
>>> [lambda x: x*x] * 3
[<function <lambda> at 0x00000000023AD2E8>, <function <lambda> at 0x00000000023AD2E8>, <function <lambda> at 0x00000000023AD2E8>]
¿Ver? 3 veces la misma lambda.
Tenga en cuenta que utilicé _comofor variable. No tiene nada que ver con el xen el lambda(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:
[lambda x: (x*x for x in range(10))]
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: un generatorretorno 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 tu lambdas ???
Bueno, entonces pasa una mierda. Mira este:
[lambda x: x * i for i in range(4)]
esto significa, por supuesto:
[(lambda x: x * i) for i in range(4)]
PERO NO 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. Un cierre léxico , por lo que se refieren a lai referencia vía, y no a su valor cuando fueron evaluados!
Entonces, 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 descompilador de Python (con lo que quiero decir, por ejemplo, el dismó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 listlambdas multiplicador, que realmente se multiplica por enteros consecutivos? Bueno, de manera similar a la respuesta aceptada, necesitamos romper el vínculo directo ienvolviéndolo en otro lambda, que se llama dentro la expresión de comprensión de la lista:
Antes de:
>>> 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
(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 lambday llamándolo inmediatamente con el valor actual de i. Otra forma (un poco más fácil de leer, ya que no produce ningún efecto 'WAT') es almacenar el valor identro de un partialobjeto y hacer que el "interno" (original) lo lambdatome como argumento (aprobado por elpartial objeto en el hora de la llamada), es decir:
Después de 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)
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 parapartial ). Hagamos un cambio de nombre:
Después de 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'
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 coefde partialesta manera, se convierte en un argumento de palabra clave, por lo que debe venir después del posicionalx argumento , así:
Después de 3:
>>> a = [partial(lambda x, coef: coef * x, coef=i) for i in (1, 2)]
>>> a[0](2), a[1](2)
(2, 4)
Preferiría la última versión sobre la lambda anidada, pero a cada una su propia ...
[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.