2011-05-20 16 views
70

¿Por qué la salida de las siguientes dos listas de comprensión es diferente, aunque f y la función lambda son las mismas?Python: función Lambda en List Comprehensions

f = lambda x: x*x 
[f(x) for x in range(10)] 

y

[lambda x: x*x for x in range(10)] 

Eso sí, tanto de tipo (f) y tipo (lambda x: x * x) devolver el mismo tipo.

+0

'[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

+0

@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

+0

@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

Respuesta

142

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 todos esos en una lista. Para que sea equivalente a la primera que necesita:

[(lambda x: x*x)(x) for x in range(10)] 

O mejor aún:

[x*x for x in range(10)] 
+5

O 'map (lambda x: x * x, rango (10))', que era probablemente lo que el OP significaba en primer lugar. –

+0

sí, lambda x: x * x .. (x) parece principio. – staticor

+0

[lambda x: x * x para x en el rango (10)] es básicamente un functur en haskell –

17

La gran diferencia es que el primer ejemplo en realidad invoca el lambda f(x), mientras que el segundo ejemplo no lo hace.

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)].

7

el primero

f = lambda x: x*x 
[f(x) for x in range(10)] 

carreras f() para cada valor en el rango de lo que hace f(x) para cada valor

el segundo

[lambda x: x*x for x in range(10)] 

corre el lambda para cada valor de la lista, por lo que genera todas esas funciones.

2

personas dieron buenas respuestas, pero se olvidó de mencionar la parte más importante en mi opinión: En el segundo ejemplo, el X de la lista por comprensión no es el mismo que el de la función Xlambda, son totalmente ajenos. lo tanto, el segundo ejemplo es en realidad lo mismo que:

[Lambda X: X*X for I in range(10)] 

Las iteraciones internas sobre range(10) sólo son responsables de la creación de 10 funciones lambda similares en una lista (10 funciones separadas pero totalmente similares - devolver el poder 2 de cada entrada)

Por otro lado, el primer ejemplo funciona de forma totalmente diferente, debido a que el X de las iteraciones interactúan con los resultados, para cada iteración el valor es X*X por lo que el resultado sería [0,1,4,9,16,25, 36, 49, 64 ,81]

+0

Este es un punto importante. Te elevé y lo expliqué en mi respuesta. –

36

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).

+3

que es una pregunta de entrevista de trabajo cruel e inusual. – szeitlin

+1

¡Mi cabeza se quemó al leer tu respuesta! – Valilutzik

+0

Si mi colega no preguntara, probablemente nunca buscaría esta respuesta – piggybox

2

Las otras respuestas son correctas, pero si usted está tratando de hacer una lista de funciones, cada uno con un parámetro diferente, que se puede ejecutar tarde, el siguiente código hará que:

import functools 
a = [functools.partial(lambda x: x*x, x) for x in range(10)] 

b = [] 
for i in a: 
    b.append(i()) 

In [26]: b 
Out[26]: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] 

mientras que el ejemplo es artificial, lo encontré útil cuando quería una lista de funciones que cada impresión algo diferente, es decir

import functools 
a = [functools.partial(lambda x: print(x), x) for x in range(10)] 

for i in a: 
    i()