Entendiendo una cadena de importaciones en Python

Sé que hay varias preguntas similares, pero me cuesta entender el error que estoy obteniendo y la navegación por los documentos y preguntas similares no han ayudado todavía. En todo caso, las preguntas similares me hacen sentir que lo que estoy haciendo es correcto.

Tengo los siguientes archivos:

src / main.py

from pack import pack if __name__ == '__main__': pack.exec("Hello Universe!") 

src / pack / pack.py

 import util def exec(text): util.write(text) if __name__ == '__main__': exec("Hello World!") 

src / pack / util.py

 def write(text): print(text) 

* src / pack / _ init _.py *

 EMPTY FILE 

Cuando ejecuto python pack.py desde el directorio src/pack , funciona (se imprime “Hello World!”). Sin embargo, cuando ejecuto python main.py desde el directorio src obtengo la siguiente excepción:

 Traceback (most recent call last): File ".../src/main.py", line 1, in  from pack import pack File ".../src/pack/pack.py", line 1, in  import util ImportError: No module named util 

Si cambio la línea de importación en pack.py a from . import util from . import util como sugerido, efectivamente ocurre lo contrario. main.py ejecuta con éxito, sin embargo ahora pack.py falla, generando:

 Traceback (most recent call last): File ".../src/pack/pack.py", line 1, in  from . import util ValueError: Attempted relative import in non-package 

Pensé que las importaciones son relativas a la ubicación actual, y como tal, debería poder construir una cadena de importaciones como esta. Me parece muy extraño que se suponga que el módulo importe un archivo hermano de forma diferente según dónde se inicie el progtwig.

¿Alguien puede explicar por qué este error se produce de una manera pero no de la otra, y si hay alguna manera de permitir que esta estructura de archivos se ejecute si quiero ejecutar desde main.py o pack.py ?

Tendrá problemas para hacer que la importación funcione en ambos casos. Esto se debe a que en un caso está ejecutando pack.py como el archivo principal y en otro lo ejecuta como parte de un paquete.

Cuando lo ejecuta como un script de python pack.py independiente, el directorio “paquete” se agrega a PYTHONPATH, lo que significa que puede importar cualquier módulo en él. Por lo tanto, la import util funcionará.

Cuando ejecuta python main.py agrega el directorio src a su PYTHONPATH. Esto significa que cualquier módulo o paquete en src , por ejemplo, el directorio del pack , ahora es importable. Por lo tanto, from pack import pack . Sin embargo, para acceder a util.py , ahora debe hacerlo from pack import util . También puedes hacer from . import util from . import util desde dentro de pack.py , como notaste.

Pero realmente no puedes hacer ambas cosas al mismo tiempo. O bien src/ es el directorio principal o src/pack es.

La solución obvia, pero incorrecta, es dejar que main.py agregue el directorio src/pack a PYTHONPATH. Eso funcionará, pero no es una buena idea. La forma correcta de hacer esto es tomar una decisión. ¿Es src/pack un módulo que debe importarse a través de un import pack o es solo una carpeta con un montón de scripts de Python? ¡Decidir! 🙂

Creo que en este caso es obvio que src/pack debería ser un módulo. Entonces, trátelo como un módulo y asegúrese de que esté disponible como un módulo. Luego puede pack.py from pack import util incluso cuando ejecute pack.py como un script principal.

¿Cómo haces eso? Bueno, básicamente, instalas el módulo del paquete en tus paquetes de sitio, o agregas el directorio src a PYTHONPATH. Eso último es lo que quieres durante el desarrollo. Puede hacerlo manualmente export PYTHONPATH= o dejar que sus analistas lo hagan por usted. ¿No tienes un probador? Pues deberías, pero esa es otra pregunta. 🙂

Para instalarlo permanentemente una vez que ya no haga el desarrollo, eche un vistazo a Distribuir . Incluye un testrunner. 😉

Su enlace es a la documentación de Python 2.7, pero parece que está usando Python 3.x.

Consulte aquí: http://docs.python.org/py3k/ para ver los documentos correctos.

Python 3 elimina la importación relativa implícita. Es decir, ya no puede importar paquetes dentro del mismo módulo de la misma manera.

Necesitas usar from . import util from . import util , esta es una importación explícitamente relativa y está permitida. import X ya no comprueba el directorio actual. En su lugar, solo verifica las entradas en sys.path. Esto incluye el directorio del script que se inició y la biblioteca estándar de python.

Falta __init__.py en el directorio de su paquete.

Cree un archivo vacío llamado __init__.py en el directorio de su paquete.

Nada en la documentación correcta apoya su teoría de que esta forma de importación debería funcionar.