Estás usando from bar import a. ase convierte en un símbolo en el ámbito global del módulo de importación (o cualquier ámbito en el que se produzca la declaración de importación).
Cuando asigna un nuevo valor a a, simplemente está cambiando qué apuntos de valor también, no el valor real. Trate de importar bar.pydirectamente con import baren __init__.pyy llevar a cabo el experimento no por el ajuste bar.a = 1. De esta manera, en realidad modificará bar.__dict__['a']cuál es el valor "real" de aen este contexto.
Es un poco complicado con tres capas, pero bar.a = 1cambia el valor de aen el módulo llamado barque en realidad se deriva __init__.py. No cambia el valor de lo aque foobarve porque foobarvive en el archivo real bar.py. Podrías establecer bar.bar.asi quisieras cambiar eso.
Este es uno de los peligros de usar la from foo import barforma de la importdeclaración: se divide baren dos símbolos, uno visible globalmente desde dentro del foocual comienza apuntando al valor original y un símbolo diferente visible en el alcance donde importse ejecuta la declaración. Cambiar a donde apunta un símbolo no cambia el valor al que apuntaba también.
Este tipo de cosas es un asesino cuando se trata de reloadun módulo del intérprete interactivo.
¡Gracias! Tu respuesta probablemente me ahorró varias horas de buscar al culpable.
jnns
Parece que incluso el bar.bar.aenfoque no ayudará mucho en la mayoría de los casos de uso. Probablemente sea una idea débil mezclar compilación o carga de módulos y asignaciones de tiempo de ejecución porque terminará confundiéndose a sí mismo (u otros que usen su código). Especialmente en lenguajes que se comportan de manera algo inconsistente al respecto, como se podría decir que lo hace Python.
matanster
26
Una fuente de dificultad con esta pregunta es que tiene un programa llamado bar/bar.py: import barimporta bar/__init__.pyo bar/bar.py, dependiendo de dónde se haga, lo que hace que sea un poco engorroso rastrear cuál aes bar.a.
Así es como funciona:
La clave para comprender lo que sucede es darse cuenta de que en su __init__.py,
from bar import a
en efecto hace algo como
a = bar.a# … where 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 ain __init__.pyvincula el nombre bar/__init__.py:aal bar.py:aobjeto original ( None). Esta es la razón por la que puede hacerlo from bar import a as a2en __init__.py: en este caso, está claro que tiene ambos bar/bar.py:ay un nombre de variable distintobar/__init__.py:a2 (en su caso, los nombres de las dos variables resultan ser ambos a, pero aún viven en espacios de nombres diferentes: en __init__.py, son bar.ay a).
Ahora, cuando lo hagas
import barprint bar.a
está accediendo a la variable bar/__init__.py:a(ya que import barimporta su bar/__init__.py). Esta es la variable que modificas (a 1). No está tocando el contenido de la variable bar/bar.py:a. Entonces, cuando lo hagas posteriormente
bar.foobar()
usted llama bar/bar.py:foobar(), que accede a la variable adesde bar/bar.py, que sigue siendo None(cuando foobar()se define, enlaza los nombres de las variables de una vez por todas, por lo que el ain bar.pyes bar.py:a, no cualquier otra avariable definida en otro módulo, ya que puede haber muchas avariables en todos los módulos importados ). De ahí la última Nonesalida.
Conclusión: es mejor evitar cualquier ambigüedad import baral no tener ningún bar/bar.pymódulo (ya bar.__init__.pyque el directorio ya es bar/un paquete, con el que también puedes importar import bar).
Dicho de otra manera: resulta que este error es muy fácil de hacer.
Se define furtivamente en la referencia del lenguaje Python: el uso de objeto en lugar de símbolo . Sugeriría que la referencia del lenguaje Python lo haga más claro y menos disperso.
El fromformulario no vincula el nombre del módulo: recorre la lista de identificadores, busca cada uno de ellos en el módulo encontrado en el paso (1) y vincula el nombre en el espacio de nombres local al objeto así encontrado.
SIN EMBARGO:
Cuando importa, importa el valor actual del símbolo importado y lo agrega a su espacio de nombres según lo definido. No está importando una referencia, efectivamente está importando 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 declaración en importJAVA, externalen C / C ++ o incluso una usecláusula en PERL.
Más bien, la siguiente declaración en Python:
from some_other_module import a as x
es más parecido al siguiente código en K&R C:
extern int a;/*importfrom the EXTERN file */
int x = a;
(advertencia: en el caso de Python, "a" y "x" son esencialmente una referencia al valor real: no está copiando el INT, está copiando la dirección de referencia)
En realidad, encuentro que la forma de Python es importmucho más limpia que la de Java, porque los espacios de nombres / ámbitos siempre se mantendrán adecuadamente separados y nunca interferirán entre sí de formas inesperadas. Como aquí: alterar un enlace de un objeto a un nombre en un espacio de nombres (léase: asignar algo a la propiedad global de un módulo) nunca afecta a otros espacios de nombres (léase: la referencia que se importó) en Python. Pero lo hace en Java, etc. En Python solo necesitas entender lo que se importa, mientras que en Java, también debes entender el otro módulo en caso de que altera este valor importado más adelante.
Tino
2
Tengo que estar totalmente en desacuerdo. Importar / incluir / usar tiene una larga historia de uso del formulario de referencia y no del formulario de valor en todos los demás idiomas.
Mark Gerolimatos
4
Maldita sea, presioné regresar ... hay una expectativa de importación por referencia (según @OP). De hecho, a un nuevo programador de Python, sin importar la experiencia que tenga, se le tiene que decir esto de la manera de "mirar hacia afuera". Eso nunca debería suceder: según 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.
bar.bar.a
enfoque no ayudará mucho en la mayoría de los casos de uso. Probablemente sea una idea débil mezclar compilación o carga de módulos y asignaciones de tiempo de ejecución porque terminará confundiéndose a sí mismo (u otros que usen su código). Especialmente en lenguajes que se comportan de manera algo inconsistente al respecto, como se podría decir que lo hace Python.Una fuente de dificultad con esta pregunta es que tiene un programa llamado
bar/bar.py
:import bar
importabar/__init__.py
obar/bar.py
, dependiendo de dónde se haga, lo que hace que sea un poco engorroso rastrear cuála
esbar.a
.Así es como funciona:
La clave para comprender lo que sucede es darse cuenta de que en su
__init__.py
,en efecto hace algo como
y define una nueva variable (
bar/__init__.py:a
, si lo desea). Por lo tanto, sufrom bar import a
in__init__.py
vincula el nombrebar/__init__.py:a
albar.py:a
objeto original (None
). Esta es la razón por la que puede hacerlofrom bar import a as a2
en__init__.py
: en este caso, está claro que tiene ambosbar/bar.py:a
y un nombre de variable distintobar/__init__.py:a2
(en su caso, los nombres de las dos variables resultan ser ambosa
, pero aún viven en espacios de nombres diferentes: en__init__.py
, sonbar.a
ya
).Ahora, cuando lo hagas
está accediendo a la variable
bar/__init__.py:a
(ya queimport bar
importa subar/__init__.py
). Esta es la variable que modificas (a 1). No está tocando el contenido de la variablebar/bar.py:a
. Entonces, cuando lo hagas posteriormenteusted llama
bar/bar.py:foobar()
, que accede a la variablea
desdebar/bar.py
, que sigue siendoNone
(cuandofoobar()
se define, enlaza los nombres de las variables de una vez por todas, por lo que ela
inbar.py
esbar.py:a
, no cualquier otraa
variable definida en otro módulo, ya que puede haber muchasa
variables en todos los módulos importados ). De ahí la últimaNone
salida.Conclusión: es mejor evitar cualquier ambigüedad
import bar
al no tener ningúnbar/bar.py
módulo (yabar.__init__.py
que el directorio ya esbar/
un paquete, con el que también puedes importarimport bar
).fuente
Dicho de otra manera: resulta que este error es muy fácil de hacer. Se define furtivamente en la referencia del lenguaje Python: el uso de objeto en lugar de símbolo . Sugeriría que la referencia del lenguaje Python lo haga más claro y menos disperso.
SIN EMBARGO:
Cuando importa, importa el valor actual del símbolo importado y lo agrega a su espacio de nombres según lo definido. No está importando una referencia, efectivamente está importando 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 declaración en
import
JAVA,external
en C / C ++ o incluso unause
cláusula en PERL.Más bien, la siguiente declaración en Python:
es más parecido al siguiente código en K&R C:
(advertencia: en el caso de Python, "a" y "x" son esencialmente una referencia al valor real: no está copiando el INT, está copiando la dirección de referencia)
fuente
import
mucho más limpia que la de Java, porque los espacios de nombres / ámbitos siempre se mantendrán adecuadamente separados y nunca interferirán entre sí de formas inesperadas. Como aquí: alterar un enlace de un objeto a un nombre en un espacio de nombres (léase: asignar algo a la propiedad global de un módulo) nunca afecta a otros espacios de nombres (léase: la referencia que se importó) en Python. Pero lo hace en Java, etc. En Python solo necesitas entender lo que se importa, mientras que en Java, también debes entender el otro módulo en caso de que altera este valor importado más adelante.