2010-12-13 14 views
5

Tengo el siguiente código Python que genera una lista de funciones anónimas:Generación de una lista de funciones en Python

basis = [ (lambda x: n*x) for n in [0, 1, 2] ]  
print basis[0](1) 

me habría esperado que fuera equivalente a

basis = [ (lambda x: 0*x), (lambda x: 1*x), (lambda x: 2*x) ] 
print basis[0](1) 

Sin embargo, mientras el segundo fragmento se imprime 0, que es lo que esperaría, las primeras impresiones 2. ¿Qué ocurre con el primer fragmento de código y por qué no se comporta como se esperaba?

+0

pregunta relacionada: http://stackoverflow.com/q/139819/4279 – jfs

Respuesta

8

Puede utilizar un parámetro por defecto para crear un cierre de n

>>> basis = [ (lambda x,n=n: n*x) for n in [0, 1, 2] ]  
>>> print basis[0](1) 
0 
+3

Esta técnica se usa con frecuencia para pasar métodos como funciones de devolución de llamada. Se parece algo a 'register_event_handler (event = evt, callback = lambda cbVar1, cbVar2, s = self: s.handle_evt (cbVar1, cbVar2))'. –

2

El problema es que en el primer ejemplo, cada lambda está vinculada al mismo n - en otras palabras, está capturando la variable, no el valor de la variable. Como n tiene el valor de 2 al final del ciclo, cada lambda usa el valor 2 para n.

parecer, usted puede usar los parámetros por defecto para resolver este problema:

basis = [ (lambda x,n=n: n*x) for n in [0, 1, 2] ] 
print basis[0](1) 

Dado que los valores de los parámetros por defecto son constantes, la n en el lado derecho de n=n serán evaluados cada vez que a través del lazo para darle un nuevo valor capturado

+0

¿Alguna sugerencia sobre cómo solucionar eso? – dzhelil

+1

celil: Edité mi respuesta para ofrecer una solución sugerida, pero no es muy elegante. – Gabe

+0

Los parámetros predeterminados pueden no ser elegantes, pero son muchísimo más fáciles de observar que la evaluación parcial explícita como solía hacer: 'basis = [(lambda n: (lambda x: n * x)) (n) para n en el rango (3)] '. :/El parámetro predeterminado no es una "constante" en el sentido de ser inmutable, pero se evalúa y se enlaza con el '.func_defaults' de lambda. Los parámetros de cierre también, pero aparentemente de una manera más mágica ... –

3

Porque es "pase por nombre".

Esto es, cuando se ejecuta el lambda, ejecuta n*x: x está obligado a 1 (es un parámetro), n se busca en el medio ambiente (es ahora 2). Por lo tanto, el resultado es 2.

+0

Esto no explica por qué se exhibe el mismo comportamiento cuando usamos una comprensión de generador en cambio, que no "gotea" 'n' en' locals() '. AFAICT, el valor de 'n' se busca desde el' .func_closure' de lambda, y no está del todo claro cómo esto se refiere mágicamente a lo que 'n' se refiere cuando' n' ya no existe. –

0

Quiero ayudar a la comprensión de la observación de Karl Knechtel (13 Dic '10 a las 7 : 32). El código siguiente muestra cómo utilizar el generador, la definición original de lambda da el resultado deseado, pero no el uso de la lista o tupla:

>>> #GENERATOR 
... basis = ((lambda x: n*x) for n in [0, 1, 2]) 
>>> print(type(basis)) 
<type 'generator'> 
>>> basis = ((lambda x: n*x) for n in [0, 1, 2]) 
>>> print([x(3) for x in basis]) 
[0, 3, 6] 
>>> #TUPLE 
... basis = tuple((lambda x: n*x) for n in [0, 1, 2]) 
>>> print(type(basis)) 
<type 'tuple'> 
>>> print([x(3) for x in basis]) 
[6, 6, 6] 
>>> #LIST 
... basis = list((lambda x: n*x) for n in [0, 1, 2]) 
>>> print(type(basis)) 
<type 'list'> 
>>> print([x(3) for x in basis]) 
[6, 6, 6] 
>>> #CORRECTED LIST 
... basis = list((lambda x, n=n: n*x) for n in [0, 1, 2]) 
>>> print(type(basis)) 
<type 'list'> 
>>> print([x(3) for x in basis]) 
[0, 3, 6] 
Cuestiones relacionadas