Script vs. Módulo
Aquí hay una explicación. La versión corta es que hay una gran diferencia entre ejecutar directamente un archivo Python e importar ese archivo desde otro lugar. El solo hecho de saber en qué directorio se encuentra un archivo no determina en qué paquete Python cree que está. Eso depende, además, de cómo se carga el archivo en Python (ejecutando o importando).
Hay dos formas de cargar un archivo Python: como el script de nivel superior o como un módulo. Un archivo se carga como script de nivel superior si lo ejecuta directamente, por ejemplo, escribiendo python myfile.py
en la línea de comando. Se carga como un módulo si lo hace python -m myfile
, o si se carga cuando import
se encuentra una declaración dentro de otro archivo. Solo puede haber un script de nivel superior a la vez; el script de nivel superior es el archivo de Python que ejecutó para comenzar.
Nombrar
Cuando se carga un archivo, se le asigna un nombre (que se almacena en su __name__
atributo). Si se cargó como script de nivel superior, su nombre es __main__
. Si se cargó como un módulo, su nombre es el nombre del archivo, precedido por los nombres de los paquetes / subpaquetes de los que forma parte, separados por puntos.
Entonces, por ejemplo, en su ejemplo:
package/
__init__.py
subpackage1/
__init__.py
moduleX.py
moduleA.py
si importó moduleX
(nota: importado , no ejecutado directamente), su nombre sería package.subpackage1.moduleX
. Si importó moduleA
, su nombre sería package.moduleA
. Sin embargo, si ejecuta directamente moduleX
desde la línea de comando, su nombre será en cambio __main__
, y si ejecuta directamente moduleA
desde la línea de comando, su nombre será __main__
. Cuando un módulo se ejecuta como script de nivel superior, pierde su nombre normal y su nombre es en su lugar __main__
.
Acceder a un módulo NO a través de su paquete contenedor
Hay una arruga adicional: el nombre del módulo depende de si se importó "directamente" desde el directorio en el que se encuentra o si se importó mediante un paquete. Esto solo hace la diferencia si ejecuta Python en un directorio e intenta importar un archivo en ese mismo directorio (o un subdirectorio del mismo). Por ejemplo, si inicia el intérprete de Python en el directorio package/subpackage1
y luego lo hace import moduleX
, el nombre de moduleX
simplemente será moduleX
, y no package.subpackage1.moduleX
. Esto se debe a que Python agrega el directorio actual a su ruta de búsqueda al inicio; si encuentra el módulo que se va a importar en el directorio actual, no sabrá que ese directorio es parte de un paquete y la información del paquete no formará parte del nombre del módulo.
Un caso especial es si ejecuta el intérprete de manera interactiva (por ejemplo, simplemente escriba python
y comience a ingresar el código Python sobre la marcha). En este caso, el nombre de esa sesión interactiva es __main__
.
Ahora aquí está lo crucial para su mensaje de error: si el nombre de un módulo no tiene puntos, no se considera parte de un paquete . No importa dónde esté realmente el archivo en el disco. Lo único que importa es cuál es su nombre, y su nombre depende de cómo lo cargó.
Ahora mire la cita que incluyó en su pregunta:
Las importaciones relativas utilizan el atributo de nombre de un módulo para determinar la posición de ese módulo en la jerarquía de paquetes. Si el nombre del módulo no contiene ninguna información del paquete (por ejemplo, está configurado como 'principal'), las importaciones relativas se resuelven como si el módulo fuera un módulo de nivel superior, independientemente de dónde se encuentre realmente el módulo en el sistema de archivos.
Importaciones relativas ...
Las importaciones relativas utilizan el nombre del módulo para determinar dónde está en un paquete. Cuando utiliza una importación relativa como from .. import foo
, los puntos indican aumentar un cierto número de niveles en la jerarquía de paquetes. Por ejemplo, si el nombre de su módulo actual es package.subpackage1.moduleX
, entonces ..moduleA
significaría package.moduleA
. Para from .. import
que funcione, el nombre del módulo debe tener al menos tantos puntos como haya en la import
declaración.
... solo son relativos en un paquete
Sin embargo, si el nombre de su módulo es __main__
, no se considera que está en un paquete. Su nombre no tiene puntos y, por lo tanto, no puede usar from .. import
declaraciones dentro de él. Si intenta hacerlo, obtendrá el error "importación relativa en un paquete no".
Los scripts no pueden importar pariente
Lo que probablemente hizo es que intentó ejecutar moduleX
o similar desde la línea de comandos. Cuando hizo esto, su nombre se estableció en __main__
, lo que significa que las importaciones relativas dentro de él fallarán, porque su nombre no revela que está en un paquete. Tenga en cuenta que esto también sucederá si ejecuta Python desde el mismo directorio donde está un módulo y luego intenta importar ese módulo, porque, como se describió anteriormente, Python encontrará el módulo en el directorio actual "demasiado pronto" sin darse cuenta de que es parte de un paquete.
Recuerde también que cuando ejecuta el intérprete interactivo, el "nombre" de esa sesión interactiva es siempre __main__
. Por lo tanto, no puede realizar importaciones relativas directamente desde una sesión interactiva . Las importaciones relativas son solo para uso dentro de los archivos del módulo.
Dos soluciones:
Si realmente desea ejecutar moduleX
directamente, pero aún desea que se considere parte de un paquete, puede hacerlo python -m package.subpackage1.moduleX
. Le -m
dice a Python que lo cargue como un módulo, no como el script de nivel superior.
O tal vez en realidad no desea ejecutar moduleX
, solo desea ejecutar algún otro script, por ejemplo myfile.py
, que use funciones dentro moduleX
. Si ese es el caso, colóquelo myfile.py
en otro lugar , no dentro del package
directorio, y ejecútelo. Si por dentro myfile.py
haces cosas como from package.moduleA import spam
, funcionará bien.
Notas
Para cualquiera de estas soluciones, el directorio del paquete ( package
en su ejemplo) debe ser accesible desde la ruta de búsqueda del módulo Python ( sys.path
). Si no es así, no podrá usar nada del paquete de manera confiable.
Desde Python 2.6, el "nombre" del módulo para propósitos de resolución de paquetes está determinado no solo por sus __name__
atributos sino también por el __package__
atributo. Es por eso que estoy evitando usar el símbolo explícito __name__
para referirme al "nombre" del módulo. Desde Python 2.6, el "nombre" de un módulo es efectivo __package__ + '.' + __name__
, o solo __name__
si lo __package__
es None
).