2010-08-21 18 views
51

Supongamos que tengo un paquete llamado bar y contiene bar.py:¿Cómo cambiar una variable de módulo desde otro módulo?

a = None 

def foobar(): 
    print a 

y __init__.py:

from bar import a, foobar 

Entonces ejecutar este script:

import bar 

print bar.a 
bar.a = 1 
print bar.a 
bar.foobar() 

Aquí es lo que espero:

None 
1 
1 

Esto es lo que obtienes:

None 
1 
None 

Puede alguien explicar mi error?

Respuesta

47

estás usando from bar import a. a se convierte en un símbolo en el alcance global del módulo de importación (o en cualquier ámbito en el que aparezca la instrucción de importación). Entonces, cuando asigna un nuevo valor a a, simplemente cambia el valor a puntos, no el valor real. intente importar bar.py directamente con import bar en __init__.py y realice su experimento allí configurando bar.a = 1. De esta forma, en realidad está modificando bar.__dict__['a'] que es el valor 'real' de a en este contexto.

Es un poco complicado con tres capas, pero bar.a = 1 cambia el valor de a en el módulo llamado bar que se deriva realmente de __init__.py. No cambia el valor de a que foobar ve porque foobar vive en el archivo real bar.py. Puede establecer bar.bar.a si desea cambiar eso.

Este es uno de los peligros de usar la forma de la declaración importfrom foo import bar: se divide bar en dos símbolos, uno visible a nivel mundial desde dentro de foo que comienza señalando el valor original y un símbolo diferente visible en el ámbito donde la instrucción import se ejecuta. Cambiar a donde señala un símbolo no cambia el valor que apuntaba también.

Este tipo de cosas es un asesino al tratar de reload un módulo del intérprete interactivo.

14

Una fuente de dificultad con esta pregunta es que usted tiene un programa llamado bar/bar.py: import bar importaciones ya sea bar/__init__.py o bar/bar.py, dependiendo de dónde se hace, que se convierte en un poco engorroso para realizar un seguimiento de lo que es abar.a.

Así es como funciona:

La clave para entender lo que ocurre es darse cuenta de que en su __init__.py,

from bar import a 

en efecto tiene algo así como

a = bar.a # … with bar = bar/bar.py (as if bar were imported locally from __init__.py) 

y define una nueva variable (bar/__init__.py:a, si lo desea). Por lo tanto, su from bar import a en __init__.py se une a la nombre bar/__init__.py:abar.py:a objeto original (None). Es por esto que se puede hacer from bar import a as a2 en __init__.py: en este caso, está claro que usted tiene tanto bar/bar.py:a y una distinta nombre de la variable bar/__init__.py:a2 (en su caso, los nombres de las dos variables que pasa es que tanto sea a, pero todavía viven en diferentes espacios de nombres: en __init__.py, son bar.a y a).

Ahora, cuando lo hace

import bar 

print bar.a 

que está accediendo a variables bar/__init__.py:a (ya import bar Las importaciones de su bar/__init__.py). Esta es la variable que modifica (a 1). No está tocando el contenido de la variable bar/bar.py:a. Así que cuando posteriormente se hace

bar.foobar() 

se llama bar/bar.py:foobar(), que accede a la variable a de bar/bar.py, que sigue siendo None (cuando se define foobar(), que une los nombres de variables de una vez por todas, por lo que el a en bar.py es bar.py:a, no a cualquier otra variable definida en otro módulo, ya que puede haber muchos a variables en todos los módulos importados). De ahí la última salida None.

5

Para dicho de otra manera: Resulta que este error es muy fácil de hacer. It is sneakily defined in the Python language reference: el uso de objeto en lugar de símbolo. Yo sugeriría que la referencia del lenguaje Python hacer esto más claro y menos escasa ..

La forma from no se une el nombre del módulo: se pasa a través de la lista de identificadores , se ve cada uno de ellos en el módulo encuentra en etapa (1), y se une el nombre en el espacio de nombres local a la objeto así encontrado.

Sin embargo:

Al importar, importar el valor actual del símbolo importado y agregarlo a su espacio de nombres como se define.No está importando una referencia, que está importando efectivamente un valor.

Por lo tanto, para obtener el valor actualizado de i, debe importar una variable que contiene una referencia a ese símbolo.

En otras palabras, la importación NO es como una declaración import en JAVA, external en C/C++ o incluso una cláusula use en PERL.

Más bien, la siguiente instrucción en Python:

from some_other_module import a as x 

es más como el código siguiente en K & RC:

extern int a; /* import from the EXTERN file */ 

int x = a; 

(advertencia: en el caso Python, "a" y "x" son esencialmente una referencia al valor real: no está copiando el INT, está copiando la dirección de referencia)

+0

En realidad, encuentro el P La forma de 'importación' de ython es mucho más limpia que la de Java, ya que los espacios de nombres/ámbitos siempre se deben mantener separados adecuadamente y nunca interferir entre sí de manera inesperada. Como aquí: Alterar un enlace de un objeto a un nombre en un espacio de nombres (leer: asignar algo a la propiedad global de un módulo) nunca afecta a otros espacios de nombres (leer: la referencia que se importó) en Python. Pero lo hace en Java, etc. En Python solo necesita comprender lo que se importa, mientras que en Java, también debe comprender el otro módulo en caso de que modifique este valor importado más adelante. – Tino

+0

Tengo que estar en desacuerdo bastante enérgicamente. La importación/inclusión/uso tiene un largo historial de uso del formulario de referencia y no del valor en casi todos los demás idiomas. –

+0

Maldita sea, presiono regresar ... hay una expectativa de importación por referencia (según @OP). De hecho, un nuevo programador de Python, sin importar cuán experimentado sea, tiene que ser dicho de esta manera "a la vista". Eso nunca debería suceder: basado en el uso común, Python tomó el camino equivocado. Cree un "valor de importación" si es necesario, pero no combine los símbolos con los valores en el momento de la importación. –

Cuestiones relacionadas