Habiendo usado ya paquetes planos, no esperaba el problema que encontré con los paquetes anidados. Aquí está…
Diseño de directorio
dir
|
+-- test.py
|
+-- package
|
+-- __init__.py
|
+-- subpackage
|
+-- __init__.py
|
+-- module.py
Contenido de init .py
Ambos package/__init__.py
y 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 forma mala e insegura de importar cosas (importar todo a granel), 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: "Ningún 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 <module 'package.subpackage.module' from '...'>
. Entonces eso es un módulo, pero eso no es un módulo / -P 8-O ... uh
Versión 3
# file test.py v3
from package.subpackage.module import attribute1
print attribute1 # OK
Este funciona. Entonces, ¿se ve obligado a usar el prefijo overkill todo el tiempo o usa la forma insegura como en la versión n. ° 1 y Python no le permite usar la forma práctica y segura? La mejor manera, que es segura y evita el prefijo largo innecesario, ¿es la única que Python rechaza? ¿Es esto porque ama import *
o porque ama los prefijos demasiado largos (lo que no ayuda a hacer cumplir esta práctica) ?.
Perdón por las palabras difíciles, pero hace dos días que intento solucionar este comportamiento estúpido. A menos que esté totalmente equivocado en alguna parte, esto me dejará con la sensación de que algo está realmente roto en el modelo de paquete y subpaquetes de Python.
Notas
- No quiero depender de
sys.path
, para evitar efectos secundarios globales, ni de*.pth
archivos, que son solo otra forma de jugarsys.path
con los mismos efectos globales. Para que la solución esté limpia, debe ser solo local. Python es capaz de manejar subpaquetes, o no, pero no debería requerir jugar con la configuración global para poder manejar cosas locales. - También intenté usar importaciones
package/subpackage/__init__.py
, pero no resolvió nada, hace lo mismo y se queja desubpackage
que no es un módulo conocido, mientras queprint subpackage
dice que es un módulo (comportamiento extraño, de nuevo).
Puede que me equivoque por completo (la opción que preferiría), pero esto me decepcionó mucho con Python.
¿Alguna otra forma conocida además de las tres que probé? ¿Algo que no conozco?
(suspiro)
-----% <----- editar ----->% -----
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 forma de administrar la referencia del paquete local.
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 varias entidades a la vez en un lote, puede:
from package.subpackage.module import attribute1, attribute2
# and etc.
No es mi primer gusto favorito (prefiero tener una declaración de importación por entidad importada), pero puede que sea la que prefiera personalmente.
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, usé:
from package.subpackage.module import (
attribute1,
attribute2,
attribute3,
...) # and etc.
__all__
variable que contenga una lista de los nombres que deben exportarse cuando se importen en estrella. editar: Bien, leyendo la respuesta de BrenBarn entendí lo que querías decir.Respuestas:
Parece que no entiende cómo
import
busca módulos. Cuando usa una declaración de importación, siempre busca la ruta del módulo real (y / osys.modules
); no hace uso de objetos de módulo en el espacio de nombres local que existen 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 importamodule
desde ese paquete. Esta línea no tiene ningún efecto sobre la tercera línea. La tercera línea solo busca un módulo llamadomodule
y no encuentra uno. No "reutiliza" el objeto llamadomodule
que obtuvo de la línea anterior.En otras palabras
from someModule import ...
, no significa "del módulo llamado someModule que importé anteriormente ..." significa "del módulo llamado someModule que se encuentra en sys.path ...". No hay forma de construir "incrementalmente" la ruta de un módulo importando los paquetes que conducen a él. Siempre debe hacer referencia al nombre completo del módulo al importar.No está claro qué está tratando de lograr. Si solo desea importar el atributo1 del objeto en particular, hágalo
from package.subpackage.module import attribute1
y termine con él. Nunca debe preocuparse por el tiempopackage.subpackage.module
una vez que haya importado el nombre que desea.Si no desea tener acceso al módulo de acceso a otros nombres más adelante, entonces puede hacerlo
from package.subpackage import module
y, como hemos visto, a continuación, puede hacermodule.attribute1
y así sucesivamente tanto como te gusta.Si desea ambos --- es decir, si desea
attribute1
acceder directamente y desea sermodule
accesible, simplemente haga las dos cosas anteriores:from package.subpackage import module from package.subpackage.module import attribute1 attribute1 # works module.someOtherAttribute # also works
Si no le gusta escribir
package.subpackage
ni siquiera dos veces, puede crear manualmente una referencia local al atributo1:from package.subpackage import module attribute1 = module.attribute1 attribute1 # works module.someOtherAttribute #also works
fuente
module.attribute1
es algo en lo que pensé, pero pensé que habría una manera de evitar la necesidad de un prefijo en todas partes. Así que tengo que usar un prefijo en todas partes, o crear un alias local, repitiendo el nombre. No es el estilo que esperaba, pero si no hay manera (después de todo, estoy acostumbrado a Ada, que requiere algo similar con sus declaraciones de cambio de nombre).La razón # 2 falla es porque
sys.modules['module']
no existe (la rutina de importación tiene su propio alcance y no puede ver elmodule
nombre local), y no hay ningúnmodule
módulo o paquete en el disco. Tenga en cuenta que puede separar varios nombres importados con comas.from package.subpackage.module import attribute1, attribute2, attribute3
También:
from package.subpackage import module print module.attribute1
fuente
sys.modules['name']
que no conocía hasta ahora, me hizo pensar que eso era lo que temía (y BrenBarn lo confirma): no hay nada como subpaquetes reales en Python.sys.modules
, como sugiere su nombre, es global, y si todas las referencias a módulos se basan en esto, entonces no hay nada como una referencia local a un módulo (¿puede venir con Python 3.x?).import
en # 2 genera una referencia local parapackage.subpackage.module
enlazarmodule
.Si todo lo que está tratando de hacer es obtener el atributo1 en su espacio de nombres global, la versión 3 parece estar bien. ¿Por qué es un prefijo exagerado?
En la versión 2, en lugar de
from module import attribute1
tu puedes hacer
fuente
attribute1 = module.attribute1
es simplemente repetir el nombre sin ningún valor agregado. Sé que funciona, pero no me gusta este estilo (lo que no implica que no me guste tu respuesta).