2012-01-19 20 views
25

Para el siguiente Python 2.7 código:Asignar a la variable de la función madre: "La variable local referenciado antes de la asignación"

#!/usr/bin/python 

def funcA(): 
    print "funcA" 
    c = 0 
    def funcB(): 
     c += 3 
     print "funcB", c 

    def funcC(): 
     print "funcC", c 

    print "c", c 
    funcB() 
    c += 2 
    funcC() 
    c += 2 
    funcB() 
    c += 2 
    funcC() 
    print "end" 

funcA() 

me sale el siguiente error:

File "./a.py", line 9, in funcB 
    c += 3 
UnboundLocalError: local variable 'c' referenced before assignment 

Pero cuando comente la línea de c += 3 en funcB, me sale el siguiente resultado:

funcA 
c 0 
funcB 0 
funcC 2 
funcB 4 
funcC 6 
end 

¿No se está accediendo a c en los dos casos de += en funcB y = en funcC? ¿Por qué no arroja error para uno pero no para el otro?

No tengo la opción de hacer c una variable global y luego declarar global c en funcB. De todos modos, el punto es no obtener c incrementado en funcB sino por qué está arrojando un error para funcB y no para funcC mientras ambos acceden a una variable local o global.

+0

pase como parámetro c ... – joaquin

+0

he modificado el código un poco ahora que ahora es la versión correcta de la pregunta. – crk

+0

Este enlace también tiene información, http://docs.python.org/faq/programming.html # why-am-i-getting-an-unboundlocalerror-when-the-variable-has-a-value –

Respuesta

49

Lo que estamos viendo aquí es la diferencia entre acceder y asignar variables En Python 2.x, solo puede asignar variables en el ámbito más interno o en el ámbito global (este último se realiza mediante el uso de la instrucción global). Puede acceder a variables en cualquier ámbito adjunto, pero no puede acceder a una variable en un ámbito adjunto y luego asignarlo en el ámbito más interno o global.

Lo que esto significa es que si hay alguna asignación a un nombre dentro de una función, ese nombre ya debe definirse en el ámbito más interno antes de acceder al nombre (a menos que se haya utilizado la declaración global).En el código de la línea c += 3 es esencialmente equivalente a la siguiente:

tmp = c 
c = tmp + 3 

Debido a que existe una asignación a c en la función, cualquier otra aparición de c en esa función sólo se verá en el ámbito local para funcB . Es por esto que ve el error, está intentando acceder al c para obtener su valor actual para el +=, pero en el alcance local c aún no se ha definido.

En Python 3 puede solucionar este problema utilizando el nonlocal statement, que le permite asignar variables que no están dentro del alcance actual, pero que tampoco están dentro del alcance global.

Su código sería algo como esto, con una línea similar en la parte superior de funcC:

def funcB(): 
     nonlocal c 
     c += 3 
     ... 

En Python 2.x esto no es una opción, y la única forma en que puede cambiar el valor de una variable no local es si es mutable.

La forma más sencilla de hacer esto es para envolver su valor en una lista, y luego modificar y acceder al primer elemento de la lista en cada lugar donde se había previamente acaba de utilizar el nombre de la variable:

def funcA(): 
    print "funcA" 
    c = [0] 
    def funcB(): 
     c[0] += 3 
     print "funcB", c[0] 

    def funcC(): 
     c[0] = 5 
     print "funcC", c[0] 

    print "c", c[0] 
    funcB() 
    funcC() 
    funcB() 
    funcC() 
    print "end" 

funcA() 

... y la salida:

no c se está accediendo
funcA 
c 0 
funcB 3 
funcC 5 
funcB 8 
funcC 5 
end 
+4

¡Qué buena explicación! El rincón oscuro de este Python para identificar el alcance por mera ** existencia ** de asignación ** en cualquier lugar ** en una función (mucho hacia el final en mi caso) simplemente me enfureció cuando busqué desesperadamente explicación en otras respuestas SO, así como Internet exterior. ¡Y me volví aún más loco cuando me encontré con algunos contraejemplos ** no ** que producen un maldito error, con explicaciones de que acceder a globales está bien en Python! ¡Finalmente, se iluminó aquí!^_^ –

6

Isn't 'c' being accessed in both cases of '+=' in funcB and '=' in funcC?

No, funcC hace una nueva variable, también llamada c. = es diferente a este respecto desde +=.

para obtener el comportamiento que (probablemente) desea, envolver la variable en una lista de un solo elemento:

def outer(): 
    c = [0] 
    def inner(): 
     c[0] = 3 
    inner() 
    print c[0] 

imprimirá 3.

Editar: Querrá pasar c como argumento. Python 2 no tiene otra manera, AFAIK, para obtener el comportamiento deseado. Python 3 introduce la palabra clave nonlocal para estos casos.

0

Prueba esto:

def funcA(): 
    print "funcA" 
    c = 0 
    def funcB(c): 
     c += 3 
     print "funcB", c 

    def funcC(c): 
     c = 5 
     print "funcC", c 

    print "c", c 
    funcB(c) 
    funcC(c) 
    funcB(c) 
    funcC(c) 
    print "end" 

funcA() 

Y si usted quiere recordar el valor c continuación:

def funcA(): 
    print "funcA" 
    c = 0 
    def funcB(c): 
     c += 3 
     print "funcB", c 
     return c 

    def funcC(c): 
     c = 5 
     print "funcC", c 
     return c 

    print "c", c 
    c = funcB(c) 
    c = funcC(c) 
    c = funcB(c) 
    c = funcC(c) 
    print "end" 

funcA() 

que producirá:

funcA 
c 0 
funcB 3 
funcC 5 
funcB 8 
funcC 5 
end 

C:\Python26\ 
2

1), en ambos casos de += en funcB y = en funcC?

No, porque c += 3 es lo mismo que:

c = c + 3 
    ^
    | 
and funcB does not know what this c is 

2) que no tienen la opción de hacer c una variable global y luego declarar global c en funcB.

Por favor, no hagas eso, basta con cambiar:

def funcB(): 

con:

def funcB(c): 

y llamar funcB(c) adelante en el código.

Nota: También debe definir cosider funcBfuncC y fuera funcA

0

Otra solución sucia, que, sin embargo, no requiere que usted haga c global. Todo lo mismo, pero:

def funcB(): 
    globals()['c'] += 3 
    print "funcB", c 
+0

bueno, todo en la forma en que se lo pide no es muy elegante, sino solo para que funcione; este enfoque funciona. – dmytro

Cuestiones relacionadas