¿Cómo cambiar una variable de módulo de otro módulo?

Supongamos que tengo un paquete llamado bar y contiene bar.py :

 a = None def foobar(): print a 

y __init__.py :

 from bar import a, foobar 

Luego ejecuto este script:

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

Esto es lo que espero:

 None 1 1 

Esto es lo que obtengo:

 None 1 None 

¿Alguien puede explicar mi error?

Usted está utilizando from bar import a . a convierte en un símbolo en el ámbito global del módulo de importación (o en cualquier ámbito en el que se produzca la statement de importación).

Cuando asigna un nuevo valor a, simplemente está cambiando qué valor es a punto, no el valor real. Intente importar bar.py directamente con la import bar en __init__.py y realice su experimento allí configurando bar.a = 1 . De esta manera, en realidad estarás modificando la 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 en realidad se deriva de __init__.py . No cambia el valor de a que foobar ve porque foobar vive en el archivo real bar.py Puedes configurar bar.bar.a si quieres cambiar eso.

Este es uno de los peligros de usar la forma de from foo import bar de la statement de import : divide la bar en dos símbolos, uno visible globalmente desde dentro de foo que comienza apuntando al valor original y un símbolo diferente visible en el ámbito donde se encuentra Se ejecuta la statement de import . Cambiar a donde apunta un símbolo no cambia el valor que apunta también.

Este tipo de cosas es un asesino cuando se intenta reload un módulo desde el intérprete interactivo.

Una fuente de dificultad con esta pregunta es que usted tiene un progtwig llamado bar/bar.py : la import bar importación importa bar/__init__.py o bar/bar.py , dependiendo de dónde se realice, lo que hace que sea un poco incómodo pista que a es bar.a

Así es como funciona:

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

 from bar import a 

en efecto hace algo 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, from bar import a en __init__.py enlaza la bar/__init__.py:a nombres bar/__init__.py:a al objeto bar.py:a original ( None ). Esta es la razón por la que puede hacer from bar import a as a2 en __init__.py : en este caso, está claro que tiene tanto bar/bar.py:a nombre de variable distinta bar/__init__.py:a2 (en su En este caso, los nombres de las dos variables solo pasan a ser a , pero aún viven en espacios de nombres diferentes: en __init__.py , son bar.a y a ).

Ahora cuando lo hagas

 import bar print bar.a 

está accediendo a la bar/__init__.py:a variables bar/__init__.py:a (ya que la import bar importación importa su bar/__init__.py ). Esta es la variable que modificas (a 1). No estás tocando el contenido de variable bar/bar.py:a . Así que cuando posteriormente haces

 bar.foobar() 

llama a bar/bar.py:foobar() , que accede a la variable a desde bar/bar.py , que todavía es None (cuando foobar() está definido, une los nombres de las variables de una vez por todas, de modo que la bar.py en bar.py es bar.py:a , no a variable definida en otro módulo, ya que puede haber muchas variables en todos los módulos importados). De ahí la última salida de None .

Dicho de otra manera: Resulta que esta idea falsa es muy fácil de hacer. Se define de forma sencilla en la referencia del lenguaje Python: el uso de objetos en lugar de símbolos . Sugeriría que la referencia del lenguaje Python haga esto más claro y menos disperso.

La forma from no vincula el nombre del módulo: recorre la lista de identificadores, busca cada uno de ellos en el módulo que se encuentra en el paso (1) y vincula el nombre en el espacio de nombres local con el objeto encontrado.

SIN EMBARGO:

Cuando importa, importa el valor actual del símbolo importado y lo agrega a su espacio de nombres como se define. No está importando una referencia, está importando efectivamente un valor.

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

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

Más bien, la siguiente statement en Python:

 from some_other_module import a as x 

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

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

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