error de scope en el cierre recursivo

¿Por qué funciona esto?

def function1(): a = 10 def function2(): print a function2() 

pero esto no lo hace:

 def function1(): a = 10 def function2(): print a a -= 1 if a>0: function2() function2() 

Me sale este error:

 UnboundLocalError: local variable 'a' referenced before assignment 

El error no parece ser muy descriptivo del problema de la raíz. Mike explica los mensajes pero eso no explica la causa raíz.

El problema real es que en Python no se puede asignar a variables cerradas. Así que en la función 2 ‘a’ es de solo lectura. Cuando le asignas, creas una nueva variable que, como Mike señala, lees antes de escribir.

Si desea asignar a la variable externa desde el ámbito interno, tiene que hacer trampa de la siguiente manera:

 def function1(): al = [10] def function2(): print al[0] al[0] -= 1 if al[0]>0: function2() function2() 

Así que al es inmutable, pero su contenido no lo es y puede cambiarlo sin crear una nueva variable.

Cabe señalar que este es un error de syntax en Python. Python en sí (a nivel de bytecode) puede asignar a estas variables muy bien; simplemente no hay syntax en 2.x para indicar que desea hacerlo. Se asume que si asigna una variable en un nivel de anidamiento, quiere decir que es un local para ella.

Este es un gran defecto; El poder asignar a los cierres es fundamental. He trabajado alrededor de esto con el hack de charlieb varias veces.

Python 3 corrige esto con la palabra clave “no local” con un nombre muy extraño:

 def function1(): a = 10 def function2(): nonlocal a print(a) a -= 1 if a>0: function2() function2() function1() 

Es muy pobre que esta syntax solo esté disponible en 3.x; la mayoría de las personas están atascadas en 2.x, y tienen que seguir usando hacks para solucionar este problema. Este mal necesita ser backported a 2.x.

http://www.python.org/dev/peps/pep-3104/

En el fragmento de código que no funciona, lo asigna a cuando dice ” a -= 1 “. Debido a eso, dentro de function2 , a es local a ese scope, no se toma el scope adjunto. Los cierres de Python son léxicos: no busca dinámicamente el marco de in function2 y, si no se le ha asignado, vaya y búsquelo en el marco de function1 .

Tenga en cuenta que esto no depende de la recursividad o el uso de cierres en absoluto. Considera el ejemplo de esta función.

 def foo(): print a a = 4 

Si lo UnboundLocalError obtendrás un UnboundLocalError también. (Sin a = 4 , usaría el global a o, si no hay uno, NameError un NameError ). Debido a que potencialmente se asigna dentro de ese scope, es local.


Si estuviera diseñando esta función, podría usar un enfoque más parecido a

 def function1(): a = 10 def function2(a=a): print a a -= 1 if a > 0: function2(a) function2() 

(o, por supuesto, for a in xrange(10, -1, -1): print a ;-))