Variables locales en funciones anidadas en Python
Vale, ten paciencia conmigo en esto, sé que va a parecer horriblemente complicado, pero por favor ayúdame a entender lo que está pasando.
from functools import partial
class Cage(object):
def __init__(self, animal):
self.animal = animal
def gotimes(do_the_petting):
do_the_petting()
def get_petters():
for animal in ['cow', 'dog', 'cat']:
cage = Cage(animal)
def pet_function():
print "Mary pets the " + cage.animal + "."
yield (animal, partial(gotimes, pet_function))
funs = list(get_petters())
for name, f in funs:
print name + ":",
f()
Da:
cow: Mary pets the cat.
dog: Mary pets the cat.
cat: Mary pets the cat.
Así que básicamente, ¿por qué no tengo tres animales diferentes? ¿No está el cage
'empaquetado' en el ámbito local de la función anidada? Si no, ¿cómo una llamada a la función anidada busca las variables locales?
Sé que encontrarse con este tipo de problemas generalmente significa que uno está "haciéndolo mal", pero me gustaría entiende lo que pasa.
3 answers
La función anidada busca variables del ámbito padre cuando se ejecuta, no cuando se define.
El cuerpo de la función se compila, y las variables 'libres' (no definidas en la propia función por asignación), se verifican, luego se unen como celdas de cierre a la función, con el código usando un índice para hacer referencia a cada celda. pet_function
por lo tanto tiene una variable libre (cage
) que luego se hace referencia a través de una celda de cierre, índice 0. El cierre en sí apunta a la variable local cage
en la función get_petters
.
Cuando realmente llamas a la función, ese cierre se usa para ver el valor de cage
en el ámbito circundante en el momento en que llamas a la función. Aquí está el problema. En el momento en que llame a sus funciones, la función get_petters
ya ha terminado de calcular sus resultados. La variable local cage
en algún momento durante esa ejecución fue asignada a cada uno de los 'cow'
, 'dog'
, y 'cat'
cadenas, pero al final de la función, cage
contiene que último valor 'cat'
. Por lo tanto, cuando llama a cada una de las funciones devueltas dinámicamente, obtiene el valor 'cat'
impreso.
La solución es no depender de cierres. Puede usar una función parcial en su lugar, crear un nuevo ámbito de función , o enlazar la variable como un valor predeterminado para un parámetro de palabra clave.
-
Ejemplo de función parcial, usando
functools.partial()
:from functools import partial def pet_function(cage=None): print "Mary pets the " + cage.animal + "." yield (animal, partial(gotimes, partial(pet_function, cage=cage)))
-
Creando un nuevo ámbito ejemplo:
def scoped_cage(cage=None): def pet_function(): print "Mary pets the " + cage.animal + "." return pet_function yield (animal, partial(gotimes, scoped_cage(cage)))
-
Enlazar la variable como un valor predeterminado para un parámetro de palabra clave:
def pet_function(cage=cage): print "Mary pets the " + cage.animal + "." yield (animal, partial(gotimes, pet_function))
No hay necesidad de definir la función scoped_cage
en el bucle, la compilación solo se lleva a cabo una vez, no en cada iteración del bucle.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-29 13:27:37
Entiendo que cage se busca en el espacio de nombres de la función padre cuando se llama realmente a pet_function cedida, no antes.
Así que cuando lo hagas
funs = list(get_petters())
Genera 3 funciones que encontrarán la última jaula creada.
Si reemplaza su último bucle con:
for name, f in get_petters():
print name + ":",
f()
En realidad obtendrás:
cow: Mary pets the cow.
dog: Mary pets the dog.
cat: Mary pets the cat.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-09-14 11:39:16
Esto se deriva de lo siguiente
for i in range(2):
pass
print i is 1
Después de iterar el valor de i
se almacena perezosamente como su valor final.
Como generador la función funcionaría (es decir, imprimiendo cada valor a su vez), pero cuando se transforma en una lista se ejecuta sobre el generador, por lo tanto, todas las llamadas a cage
(cage.animal
) devuelve los gatos.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-05 17:06:51