Lambdas de una lista de comprensión están devolviendo un lambda cuando se llama

Estoy intentando iterar la función lambda en una lista como en test.py , y quiero obtener el resultado de la llamada de la lambda, no el objeto de función en sí. Sin embargo, la siguiente salida realmente me confundió.

 ------test.py--------- #!/bin/env python #coding: utf-8 a = [lambda: i for i in range(5)] for i in a: print i() --------output--------- <function  at 0x7f489e542e60> <function  at 0x7f489e542ed8> <function  at 0x7f489e542f50> <function  at 0x7f489e54a050> <function  at 0x7f489e54a0c8> 

Modifiqué el nombre de la variable cuando imprimí el resultado de la llamada a t como sigue, y todo va bien. Me pregunto qué se trata de eso. ?

 --------test.py(update)-------- a = [lambda: i for i in range(5)] for t in a: print t() -----------output------------- 4 4 4 4 4 

En Python 2, la comprensión de la lista ‘filtra’ las variables al ámbito externo:

 >>> [i for i in xrange(3)] [0, 1, 2] >>> i 2 

Tenga en cuenta que el comportamiento es diferente en Python 3:

 >>> [i for i in range(3)] [0, 1, 2] >>> i Traceback (most recent call last): File "", line 1, in  NameError: name 'i' is not defined 

Cuando define lambda, está vinculado a la variable i , no a su valor actual como muestra su segundo ejemplo. Ahora, cuando asigne un nuevo valor a i la lambda devolverá cualquiera que sea el valor actual:

 >>> a = [lambda: i for i in range(5)] >>> a[0]() 4 >>> i = 'foobar' >>> a[0]() 'foobar' 

Dado que el valor de i dentro del bucle es la lambda, lo obtendrá como valor de retorno:

 >>> i = a[0] >>> i()  at 0x01D689F0> >>> i()()()()  at 0x01D689F0> 

ACTUALIZACIÓN : Ejemplo en Python 2.7:

 Python 2.7.6 (default, Jun 22 2015, 17:58:13) [GCC 4.8.2] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> a = [lambda: i for i in range(5)] >>> for i in a: ... print i() ...  at 0x7f1eae7f15f0>  at 0x7f1eae7f1668>  at 0x7f1eae7f16e0>  at 0x7f1eae7f1758>  at 0x7f1eae7f17d0> 

Lo mismo en Python 3.4:

 Python 3.4.3 (default, Oct 14 2015, 20:28:29) [GCC 4.8.4] on linux Type "help", "copyright", "credits" or "license" for more information. >>> a = [lambda: i for i in range(5)] >>> for i in a: ... print(i()) ... 4 4 4 4 4 

Para obtener detalles sobre el cambio con respecto al scope variable con comprensión de lista, consulte la publicación de blog de Guido del 2010 .

También hicimos otro cambio en Python 3, para mejorar la equivalencia entre las comprensiones de listas y las expresiones generadoras. En Python 2, la comprensión de la lista “filtra” la variable de control de bucle en el ámbito circundante:

 x = 'before' a = [x for x in 1, 2, 3] print x # this prints '3', not 'before' 

Sin embargo, en Python 3, decidimos arreglar el “pequeño secreto sucio” de las comprensiones de listas usando la misma estrategia de implementación que para las expresiones generadoras. Por lo tanto, en Python 3, el ejemplo anterior (después de la modificación para usar print (x) 🙂 se imprimirá ‘antes’, lo que demuestra que la ‘x’ en la lista de comprensión se oscurece temporalmente pero no anula la ‘x’ en el entorno scope.

Los cierres en Python son vinculantes tardíos , lo que significa que cada función lambda en la lista solo evaluará la variable i cuando se invoque, y no cuando esté definida. Es por eso que todas las funciones devuelven el mismo valor, es decir, el último valor de ì (que es 4).

Para evitar esto, una técnica es vincular el valor de i a un parámetro local denominado:

 >>> a = [lambda i=i: i for i in range(5)] >>> for t in a: ... print t() ... 0 1 2 3 4 

Otra opción es crear una función parcial y vincular el valor actual de i como su parámetro:

 >>> from functools import partial >>> a = [partial(lambda x: x, i) for i in range(5)] >>> for t in a: ... print t() ... 0 1 2 3 4 

Edit: Disculpe, malinterprete la pregunta inicialmente, ya que este tipo de preguntas son tan a menudo sobre un enlace tardío (gracias @soon por el comentario).

La segunda razón para el comportamiento es la pérdida de variables de comprensión de lista en Python2 como ya explicaron otros. Cuando se utiliza i como la variable de iteración en el bucle for , cada función imprime el valor actual de i (por las razones indicadas anteriormente), que es simplemente la función en sí. Cuando se usa un nombre diferente (por ejemplo, t ), las funciones imprimen el último valor de i como estaba en el bucle de comprensión de lista, que es 4.

lambda: i es una función anónima sin argumentos que devuelve i. Por lo tanto, está generando una lista de funciones anónimas, que más adelante (en el segundo ejemplo) puede vincular al nombre t e invocar con () . Tenga en cuenta que puede hacer lo mismo con funciones no anónimas:

 >>> def f(): ... return 42 ... >>> name = f >>> name  >>> name() 42 

@plamut acaba de responder la otra parte implícita de la pregunta, así que no lo haré.