2011-07-26 16 views
6

Eche un vistazo a este código:Python y cerradas las variables

def closure(): 
    value = False 

    def method_1(): 
     value = True 

    def method_2(): 
     print 'value is:', value 

    method_1() 
    method_2() 

closure() 

Yo esperaría que imprime 'El valor es: True', pero no lo hace. ¿Por qué es esto y cuál es la solución?

+0

Este hilo es un ejemplo perfecto de por qué los cinco minutos de edición gratuita a veces son malos. – agf

+0

@agf: ¿Por qué es malo? – Cameron

Respuesta

16

Esto ocurre porque method_1 tiene su propio alcance local donde puede declarar variables. Python ve value = True y cree que está creando una nueva variable llamada value, local en method_1.

La razón por la que Python hace esto es para evitar contaminar las entidades locales del osciloscopio externo con variables de una función interna. (¡No desearía que las asignaciones en funciones regulares a nivel de módulo den como resultado la creación de variables globales!)

Si no asigna value, Python busca los ámbitos externos buscando la variable (entonces leyendo la variable funciona como se esperaba, como lo demuestra su method_2).

Una forma de evitar esto es mediante el uso de un objeto mutable en lugar de assigment:

result = { 'value': False } 

def method_1(): 
    result['value'] = True 

En Python 3, el nonlocal statement (ver también docs) se añadió precisamente para este escenario:

def method_1(): 
    nonlocal value 
    value = True # Works as expected -- assigns to `value` from outer scope 
2

En method_1, Python asume (con bastante sensatez) que value es una variable local. Siempre que asigne un nombre de variable dentro de una función, se supone que ese nombre de variable es una nueva variable local. Si quieres que sea global, debes declararlo como global, y si quieres que sea "no local", en Python 3, puedes declararlo nonlocal, pero en Python 2, tienes que hacer algo más feo: almacena el valor en un contenedor. Eso evita tener que reasignar el nombre de la variable, y así evita la ambigüedad del alcance.

def method_1_global(): 
    global value 
    value = True 

def method_1_nonlocal_P3(): 
    nonlocal value 
    value = True 

value = [False] 
def method_1_nonlocal_P2(): 
    value[0] = True 
+0

Tenga en cuenta que con 'global' ya no forma parte del cierre y puede dañarse en otro lugar del programa. – agf

+0

@agf - bastante; Solo trato de explicar las reglas de alcance de la manera más general posible. – senderle

2

Cuando asigna una variable, asume que la variable es de ámbito local. Por lo tanto, el value en method_1 no es el value en closure.

Si desea que esto funcione en Python 3, agregue una línea a method_1: nonlocal value.

En Python 2,

def closure(): 
    value = [False] 

    def method_1(): 
     value[0] = True 

    def method_2(): 
     print 'value is:', value 

    method_1() 
    method_2() 

closure() 

es una posible solución alternativa.

2

Esto se debe a que en realidad no se ha modificado la variable cerrada, la está enmascarando con una nueva que tiene el mismo nombre. En Python 2.x no hay una manera fácil de solucionar esto, por lo que la palabra clave nonlocal se agregó en python 3.

Sin embargo, esto se puede solucionar utilizando tipos variables como la lista y el diccionario. Hay un good example in this answer.

1

Para evitar esto, puede usar una lista.

value = [False] 

def method_1(): 
    value[0] = True 

Lo Python hace ahora es la búsqueda de los niveles superiores del alcance que valor no está disponible en las variables locales. Como valor es una lista y Python lo refiere como una variable global relativa a * method_1 *, puede tratar el valor como, una lista.

Cuestiones relacionadas