2010-03-25 20 views
8

por qué funciona esto:error de alcance en el cierre recursivo

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

pero esto no es así:

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 

Respuesta

13

no parece el error de ser muy descriptivo del problema de 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 las variables cerradas. Entonces, en la función 2, 'a' es de solo lectura. Cuando lo asigna, crea una nueva variable que, como señala Mike, lee antes de escribir.

Si desea asignar a la de la variable externa del ámbito interno que tiene que engañar de esta manera:

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

Así AL es inmutable, pero su contenido no son y se puede cambiar sin crear una nueva variable.

+2

De hecho, este es el punto clave en el diseño de esta función no se puede asignar a ámbitos no locales. (Nota: 'al' es * mutable *; por eso funciona). –

+2

Creo que es importante, para mayor claridad, distinguir entre al variable y los valores que contiene. Siempre regresa a los indicadores para mí, así que déjame decir esto; no puede hacer que apunte a una nueva lista, pero puede cambiar el contenido de la lista a lo que apunta. al -> [v1, v2, v3] al no se puede cambiar, pero v1, v2 y v3 se pueden cambiar. Mike tiene toda la razón de que esto hace que todo sea mutable porque en nuestra terminología al * es * la lista no es el puntero a la lista. – charlieb

+0

+1 muy buena respuesta. –

2

En el fragmento que no funciona , asigne al a cuando diga "a -= 1". Por eso, dentro de function2, a es local para ese alcance, no tomado el alcance adjunto. Los cierres de Python son léxicos: no busca dinámicamente a en el marco de function2 y, si no se 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. Considere el ejemplo de esta función

def foo(): 
    print a 
    a = 4 

Calling le conseguirá un UnboundLocalError también. (Sin a = 4, utilizaría el a global o, si no hay uno, generaría un NameError). Dado que a está potencialmente asignado dentro de ese alcance, es local.


Si estaba diseñando esta función, puede ser que utilice un enfoque más como

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

5

Se debe tener en cuenta que este es un error de sintaxis en Python. Python mismo (en el nivel de bytecode) puede asignar a estas variables muy bien; simplemente no hay sintaxis en 2.x para indicar que quieres hacerlo. Supone que si asigna una variable en un nivel de anidación, quiere decir que es local.

Esta es una gran deficiencia; poder asignar cierres es fundamental. He trabajado alrededor de esto con el truco de charlieb varias veces.

Python 3 permite reparar este problema con la misma torpeza con nombre en clave "no local":

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

Es muy pobre que esta sintaxis sólo está disponible en 3.x; la mayoría de las personas están atrapadas en 2.x y tienen que seguir utilizando hacks para evitar este problema.Esto necesita ser backported a 2.x.

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

Cuestiones relacionadas