El ‘objeto de módulo condicional de Python no tiene ningún atributo’ error con un paquete personal distinto del problema de importación circular

“Aparece un error en el objeto de módulo. No tengo ningún atributo …” al intentar utilizar una jerarquía de paquetes que creé. El error recuerda el error que se produce cuando hay una importación circular (es decir, el módulo a importa by el módulo b Importa a), pero no puedo ver el problema aquí. He pasado por muchos mensajes con un error similar, pero ninguna de las explicaciones que vi era adecuada.

Esto se vio con python 2.7.1 y python 2.4.3.

Lo he diluido hasta el siguiente ejemplo:

Considere la siguiente jerarquía (vea el código a continuación):

alpha alpha/__init__.py alpha/bravo alpha/bravo/__init__.py alpha/bravo/charlie.py alpha/bravo/delta.py alpha/bravo/echo.py 

El módulo charlie importa echo que a su vez importa delta. Si el alpha / bravo / __ init__.py (como alpha / __ init__.py) está esencialmente en blanco, un script puede hacer:

 import alpha.bravo.charlie 

El problema surge si trato de importar alpha.bravo.charlie en alpha / bravo / __ init__.py (con el pensamiento de que podría surgir clases / métodos relevantes allí, y un script haría ‘importar alpha.bravo’).

Código:

alpha / __ init__.py

 (blank) 

alpha / bravo / __ init__.py

 import alpha.bravo.charlie 

alpha / bravo / charlie.py

 import alpha.bravo.echo def charlie_foo(x): return str(x) def charlie_bar(x): return alpha.bravo.echo.echo_biz() 

alpha / bravo / delta.py

 def delta_foo(x): return str(x) 

alpha / bravo / echo.py

 import alpha.bravo.delta print alpha.bravo.delta.delta_foo(1) def echo_biz(): return 'blah' 

Si lo bash

 python -c 'import alpha.bravo' 

Yo obtengo:

 Traceback (most recent call last): File "", line 1, in  File "/home/kmkc980/svn/working/azcif/python/lib/alpha/bravo/__init__.py", line 1, in  import alpha.bravo.charlie File "/home/kmkc980/svn/working/azcif/python/lib/alpha/bravo/charlie.py", line 1, in  import alpha.bravo.echo File "/home/kmkc980/svn/working/azcif/python/lib/alpha/bravo/echo.py", line 2, in  print alpha.bravo.delta.delta_foo(1) AttributeError: 'module' object has no attribute 'bravo' 

Pero, si comento la línea de importación en alpha / bravo / __ init__.py, entonces todo parece estar bien:

 python -c 'import alpha.bravo' python -c 'import alpha.bravo.charlie' 1 

Además, si uso el mismo código anterior (incluida la línea de importación en alpha / bravo / __ init__.py), pero edito todo para excluir el nivel ‘alpha’ de la jerarquía, parece funcionar bien.

Así que la jerarquía es ahora:

 bravo bravo/__init__.py bravo/charlie.py bravo/delta.py bravo/echo.py 

y cambio todas las líneas con “alpha.bravo. *” a “bravo. *”

Entonces no hay problema:

 python -c 'import bravo' 1 

He podido solucionar el problema, pero todavía me gustaría entenderlo. Gracias.

Aquí está el por qué

(Creo que esto está respaldado principalmente por la explicación en http://docs.python.org/faq/programming.html#how-can-i-have-modules-that-mutually-import-each-other-other )

Cuando el intérprete de Python encuentra una línea del formulario import abc , ejecuta los siguientes pasos. En pseudo-python:

 for module in ['a', 'a.b', 'abc']: if module not in sys.modules: sys.modules[module] = (A new empty module object) run every line of code in module # this may recursively call import add the module to its parent's namespace return module 'a' 

Hay tres puntos importantes aquí:

  1. Los módulos a, ab y abc se importan en orden, si aún no se han importado

  2. Un módulo no existe en el espacio de nombres de su padre hasta que haya terminado de importarse por completo. Así que el módulo a no tiene un atributo b hasta que ab se haya importado completamente.

  3. No importa qué tan profunda sea su cadena de módulos, incluso si import abcdefg , su código solo obtiene un símbolo agregado a su espacio de nombres: a .

    Entonces, cuando más tarde intentes ejecutar abcdefgsome_function() , el intérprete tiene que atravesar toda la cadena de módulos para llegar a ese método.

Esto es lo que está sucediendo.

Según el código que ha publicado, el problema parece estar en la statement de impresión en alpha/bravo/echo/__init__.py . Lo que el intérprete ha hecho para cuando llega allí es aproximadamente esto:

  1. Configurar un objeto de módulo vacío para alfa en sys.modules

  2. Ejecute el código en alfa / __ init__.py (tenga en cuenta que dir (alfa) no contendrá ‘bravo’ en este punto)

  3. Configure un objeto de módulo vacío para alpha.bravo en sys.modules

  4. Ejecuta el código en alpha / bravo / __ init__.py:

    4.1 Configure un objeto de módulo vacío para alpha.bravo.charlie en sys.modules

    4.2 Ejecuta el código en alpha / bravo / charlie / __ init__.py:

    4.2.1 Configure un objeto de módulo vacío para alpha / bravo / echo en sys.modules

    4.2.2 Ejecute el código en alpha / bravo / echo / __ init__.py:

    4.2.2.1 Configure un objeto de módulo vacío para alpha / bravo / delta en sys.modules

    4.2.2.2 Ejecute el código en alpha / bravo / delta / __ init__.py – Esto termina, por lo que se agrega ‘delta’ a los símbolos de ‘alpha.bravo.

    4.2.2.3 Añadir ‘alfa’ a los símbolos de eco. Este es el último paso en la import alpha.bravo.delta .

En este punto, si llamamos a dir () en todos los módulos en sys.modules, veremos esto:

  • ‘alpha’: ['__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', '__return__'] (esto está esencialmente vacío)

  • ‘alpha.bravo’: ['__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', 'delta'] (el delta ha terminado de importarse, por lo que está aquí)

  • ‘alpha.bravo.charlie’: ['__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__'] (vacío)

  • ‘alpha.bravo.delta’: ['__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', '__return__', 'delta.foo'] (Este es el único que ha completado)

  • ‘alpha.bravo.echo’: ['__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', '__return__', 'alpha']

Ahora el intérprete continúa con alpha / bravo / echo / __ init__.py, donde encuentra la línea de print alpha.bravo.delta.delta_foo(1) . Eso comienza esta secuencia:

  1. obtener la variable global alpha – esto devuelve el módulo alpha aún vacío.
  2. llame a getattr (alfa, ‘bravo’): esto falla porque la alpha.bravo no se ha iniciado, así que bravo no se ha insertado en la tabla de símbolos del alfa.

Esto es lo mismo que sucede durante una importación circular: el módulo no ha terminado de inicializarse, por lo que la tabla de símbolos no se actualiza completamente y el acceso a los atributos falla.

Si tuviera que reemplazar la línea ofensiva en echo / __ init__.py con esto:

 import sys sys.modules['alpha.bravo.delta'].delta_foo(1) 

Eso probablemente funcionaría, ya que el delta está completamente inicializado. Pero hasta que se complete bravo (después de que echo y charlie regresen), la tabla de símbolos para alpha no se actualizará, y no podrá acceder a bravo a través de ella.

Además, como dice @Ric Poggi, si cambia la línea de importación a

 from alpha.bravo.delta import delta_foo 

Entonces eso funcionará. En este caso, dado que from alpha.bravo.delta va from alpha.bravo.delta a sys.modules dict, en lugar de atravesar de alpha a bravo a delta, puede obtener la función del módulo delta y asignarla a una variable local, que puede A continuación, acceder sin ningún problema.

En lugar de usar importaciones absolutas, podría ayudar usar parientes.

es decir

alpha / bravo / _ init _.py

 import alpha.bravo.charlie 

debiera ser

 import charlie 

De lo contrario, es probable que sea una importación circular. es decir, si importas alpha.bravo.charlie desde charlie, eso significa

 alpha/__init__.py bravo/__init__.py charlie/__init__.py 

Todos se cargan (o, mejor dicho, se les impide hacerlo porque ya están cargados). Eso podría causar el problema que estás viendo.