Python: importar un subpaquete o un submódulo

Como ya había utilizado paquetes planos, no esperaba el problema que encontré con los paquetes nesteds. Aquí está…

Diseño de directorio

dir | +-- test.py | +-- package | +-- __init__.py | +-- subpackage | +-- __init__.py | +-- module.py 

Contenido de init .py

Tanto el package/__init__.py como el package/subpackage/__init__.py están vacíos.

Contenido de module.py

 # file `package/subpackage/module.py` attribute1 = "value 1" attribute2 = "value 2" attribute3 = "value 3" # and as many more as you want... 

Contenido de test.py (3 versiones)

Versión 1

 # file test.py from package.subpackage.module import * print attribute1 # OK 

Esa es la manera mala e insegura de importar cosas (importar todo en forma masiva), pero funciona.

Versión 2

 # file test.py import package.subpackage.module from package.subpackage import module # Alternative from module import attribute1 

Una forma más segura de importar, elemento por elemento, pero falla, Python no quiere esto: falla con el mensaje: “No hay módulo llamado módulo”. Sin embargo …

 # file test.py import package.subpackage.module from package.subpackage import module # Alternative print module # Surprise here 

… dice . Entonces eso es un módulo, pero eso no es un módulo / -P 😯 … uh

Versión 3

 # file test.py v3 from package.subpackage.module import attribute1 print attribute1 # OK 

Este funciona ¿Entonces, o se ven obligados a usar el prefijo de exageración todo el tiempo o usar la forma insegura como en la versión 1 y Python no le permite usar la forma segura y práctica? La mejor manera, que sea segura y evite prefijos largos innecesarios, ¿es la única que Python rechaza? ¿Esto se debe a que ama import * o porque ama los prefijos demasiado largos (lo que no ayuda a hacer cumplir esta práctica)?

Lo siento por las palabras difíciles, pero son dos días que trato de evitar este comportamiento de estupidez. A menos que estuviera totalmente equivocado en algún lugar, esto me dejará con la sensación de que algo está realmente roto en el modelo de paquetes y subpaquetes de Python.

Notas

  • No quiero confiar en sys.path , para evitar los efectos secundarios globales, ni en los archivos *.pth , que son solo otra forma de jugar con sys.path con los mismos efectos globales. Para que la solución sea limpia, debe ser solo local. Python puede manejar subpaquetes, o no, pero no debería requerir jugar con la configuración global para poder manejar cosas locales.
  • También intenté usar las importaciones en el package/subpackage/__init__.py , pero no resolvió nada, hace lo mismo, y se queja de que el subpackage no es un módulo conocido, mientras que el print subpackage dice que es un módulo (comportamiento extraño, nuevamente).

Puede ser que estoy completamente equivocado, duro (la opción que preferiría), pero esto me hace sentir muy decepcionado con Python.

¿Algún otro camino conocido al lado de los tres que probé? Algo que no sé?

(suspiro)

—–% % —–

Conclusión hasta ahora (después de los comentarios de la gente)

No hay nada como un subpaquete real en Python, ya que todas las referencias de paquetes van a un diccionario global, solo, lo que significa que no hay un diccionario local, lo que implica que no hay manera de administrar las referencias de paquetes locales.

Tienes que usar prefijo completo o prefijo corto o alias. Como en:

Versión de prefijo completo

 from package.subpackage.module import attribute1 # An repeat it again an again # But after that, you can simply: use_of (attribute1) 

Versión de prefijo corto (pero prefijo repetido)

 from package.subpackage import module # Short but then you have to do: use_of (module.attribute1) # and repeat the prefix at every use place 

O bien, una variación de lo anterior.

 from package.subpackage import module as m use_of (m.attribute1) # `m` is a shorter prefix, but you could as well # define a more meaningful name after the context 

Versión factorizada

Si no le importa importar múltiples entidades a la vez en un lote, puede:

 from package.subpackage.module import attribute1, attribute2 # and etc. 

No estoy en mi primer gusto favorito (prefiero tener una statement de importación por entidad importada), pero puede ser la que personalmente favoreceré.

Actualización (2012-09-14):

Finalmente, parece estar bien en la práctica, excepto con un comentario sobre el diseño. En lugar de lo anterior, utilicé:

 from package.subpackage.module import ( attribute1, attribute2, attribute3, ...) # and etc. 

Parece que estás malinterpretando cómo la import busca módulos. Cuando utiliza una statement de importación, siempre busca la ruta del módulo real (y / o sys.modules ); no hace uso de objetos de módulo en el espacio de nombres local que existe debido a importaciones anteriores. Cuando tu lo hagas:

 import package.subpackage.module from package.subpackage import module from module import attribute1 

La segunda línea busca un paquete llamado package.subpackage e importa el module de ese paquete. Esta línea no tiene efecto en la tercera línea. La tercera línea solo busca un módulo llamado module y no encuentra uno. No “reutiliza” el objeto llamado module que obtuvo de la línea anterior.

En otras palabras, from someModule import ... no significa “desde el módulo llamado Módulo que importé anteriormente …” significa “desde el módulo llamado Módulo que encuentra en sys.path …”. No hay manera de “incrementar” la ruta de un módulo importando los paquetes que lo conducen. Siempre debe referirse al nombre completo del módulo al importar.

No está claro lo que estás tratando de lograr. Si solo desea importar el atributo del objeto en particular1, solo hágalo from package.subpackage.module import attribute1 y termine con él. Nunca debe preocuparse por el largo package.subpackage.module una vez que haya importado el nombre que desea de él.

Si desea tener acceso al módulo para acceder a otros nombres más adelante, puede hacerlo from package.subpackage import module y, como ha visto, puede hacer module.attribute1 y así sucesivamente todo lo que desee.

Si desea ambos — es decir, si desea un attribute1 directamente accesible y desea que el module accesible, solo haga lo siguiente:

 from package.subpackage import module from package.subpackage.module import attribute1 attribute1 # works module.someOtherAttribute # also works 

Si no te gusta escribir package.subpackage incluso dos veces, puedes crear manualmente una referencia local para attribute1:

 from package.subpackage import module attribute1 = module.attribute1 attribute1 # works module.someOtherAttribute #also works 

La razón # 2 falla es porque sys.modules['module'] no existe (la rutina de importación tiene su propio scope y no puede ver el nombre local del module ), y no hay un módulo o paquete de módulos en el disco. Tenga en cuenta que puede separar varios nombres importados por comas.

 from package.subpackage.module import attribute1, attribute2, attribute3 

También:

 from package.subpackage import module print module.attribute1 

Si todo lo que está intentando hacer es obtener el atributo 1 en su espacio de nombres global, la versión 3 parece estar bien. ¿Por qué es un prefijo excesivo?

En la versión 2, en lugar de

 from module import attribute1 

tu puedes hacer

 attribute1 = module.attribute1