Yo he estado aquí:
- http://www.python.org/dev/peps/pep-0328/
- http://docs.python.org/2/tutorial/modules.html#packages
- Paquetes de Python: importaciones relativas
- el código de ejemplo de importación relativa de Python no funciona
- Respuesta definitiva a las importaciones relativas de Python
- Importaciones relativas en Python
- Python: deshabilitar la importación relativa
y muchas URL que no copié, algunas en SO, otras en otros sitios, cuando pensé que tendría la solución rápidamente.
La pregunta que siempre se repite es esta: con Windows 7, Python 2.7.3 de 32 bits, ¿cómo resuelvo este mensaje "Intento de importación relativa en un paquete que no es de paquete"? Construí una réplica exacta del paquete en pep-0328:
package/
__init__.py
subpackage1/
__init__.py
moduleX.py
moduleY.py
subpackage2/
__init__.py
moduleZ.py
moduleA.py
Las importaciones se realizaron desde la consola.
Realicé funciones llamadas spam y huevos en sus módulos apropiados. Naturalmente, no funcionó. Aparentemente, la respuesta está en la cuarta URL que enumeré, pero para mí todo es ex alumnos. Hubo esta respuesta en una de las URL que visité:
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.
La respuesta anterior parece prometedora, pero todos son jeroglíficos para mí. Entonces, mi pregunta, ¿cómo hago para que Python no me devuelva "Intento de importación relativa en un paquete no"? tiene una respuesta que implica -m, supuestamente.
¿Puede alguien decirme por qué Python da ese mensaje de error, qué significa "sin paquete", por qué y cómo define un "paquete" y la respuesta precisa expresada en términos lo suficientemente fáciles de entender para un niño de kindergarten .
fuente
def spam(): pass
, moduleA.py tienedef eggs(): pass
. Traté de ejecutar un par de comandos "desde .algo importar algo", pero no funcionaron. Nuevamente, vea pep-0328.from .something import something
en el intérprete interactivo, eso no funcionará. Las importaciones relativas solo se pueden usar dentro de los módulos, no de forma interactiva.Respuestas:
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 hacepython -m myfile
, o si se carga cuandoimport
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:
si importó
moduleX
(nota: importado , no ejecutado directamente), su nombre seríapackage.subpackage1.moduleX
. Si importómoduleA
, su nombre seríapackage.moduleA
. Sin embargo, si ejecuta directamentemoduleX
desde la línea de comando, su nombre será en cambio__main__
, y si ejecuta directamentemoduleA
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 haceimport moduleX
, el nombre demoduleX
simplemente serámoduleX
, y nopackage.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:
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 espackage.subpackage1.moduleX
, entonces..moduleA
significaríapackage.moduleA
. Parafrom .. import
que funcione, el nombre del módulo debe tener al menos tantos puntos como haya en laimport
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 usarfrom .. 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 hacerlopython -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 ejemplomyfile.py
, que use funciones dentromoduleX
. Si ese es el caso, colóquelomyfile.py
en otro lugar , no dentro delpackage
directorio, y ejecútelo. Si por dentromyfile.py
haces cosas comofrom 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__
esNone
).fuente
Its name has no dots, and therefore you cannot use from .. import statements inside it. If you try to do so, you will get the "relative-import in non-package" error.
Esto es fundamentalmente perturbador. ¿Qué tiene de difícil mirar el directorio actual? Python debería ser capaz de esto. ¿Está esto arreglado en la versión 3x?__package__ = 'package.subpackage1'
o cosas similares. A continuación, ese archivo será siempre ser considerada como parte de ese paquete, incluso si se ejecuta directamente. Si tiene otras preguntas sobre__package__
usted, es posible que desee hacer una pregunta por separado, ya que nos estamos librando del tema de su pregunta original aquí.__name__
ysys.path
. Específicamente, conpython -m pkg.mod
,__name__
se establece en__main__
, nopkg.mod
; las importaciones relativas se resuelven utilizando en__package__
lugar de__name__
en este caso. Además, Python agrega el directorio del script en lugar del directorio actualsys.path
cuando se ejecutapython path/to/script.py
; agrega el directorio actualsys.path
cuando se ejecuta en la mayoría de los otros modos, incluidopython -m pkg.mod
.Esto es realmente un problema dentro de Python. El origen de la confusión es que las personas toman por error la importación relativa como ruta relativa, que no lo es.
Por ejemplo, cuando escribes en faa.py :
Esto tiene un significado solo si faa.py fue identificado y cargado por python, durante la ejecución, como parte de un paquete. En ese caso, el nombre del módulo para faa.py sería, por ejemplo, some_packagename.faa . Si el archivo se cargó solo porque está en el directorio actual, cuando se ejecuta python, su nombre no se referiría a ningún paquete y, finalmente, la importación relativa fallaría.
Una solución simple para referir módulos en el directorio actual es usar esto:
fuente
from __future__ import absolute_import
y obligar al usuario a usar su código correctamente ... para que siempre pueda hacerlofrom . import foo
Aquí hay una receta general, modificada para ajustarse como ejemplo, que estoy usando en este momento para tratar con las bibliotecas de Python escritas como paquetes, que contienen archivos interdependientes, donde quiero poder probar partes de ellas por partes. Llamemos a esto
lib.foo
y digamos que necesita accesolib.fileA
para las funcionesf1
yf2
, ylib.fileB
para la claseClass3
.He incluido algunas
print
llamadas para ayudar a ilustrar cómo funciona esto. En la práctica, querrás eliminarlos (y quizás también lafrom __future__ import print_function
línea).Este ejemplo particular es demasiado simple para mostrar cuándo realmente necesitamos insertar una entrada en
sys.path
. (Vea la respuesta de Lars para un caso en el que lo necesitamos, cuando tenemos dos o más niveles de directorios de paquetes, y luego usamos,os.path.dirname(os.path.dirname(__file__))
pero tampoco hace daño aquí). También es lo suficientemente seguro para hacer esto sin elif _i in sys.path
prueba. Sin embargo, si cada archivo importado se inserta el mismo camino; por ejemplo, si ambosfileA
yfileB
quieren utilidades de importación de los paquetes-este estorba hastasys.path
con el mismo camino muchas veces, por lo que es bueno tener elif _i not in sys.path
en el texto modelo.La idea aquí es esta (y tenga en cuenta que todos estos funcionan de la misma manera en python2.7 y python 3.x):
Si se ejecuta como
import lib
ofrom lib import foo
como una importación de paquete normal desde el código ordinario,__package
eslib
y__name__
eslib.foo
. Tomamos la primera ruta de código, importando desde.fileA
, etc.Si se ejecuta como
python lib/foo.py
,__package__
será None y__name__
será__main__
.Tomamos la segunda ruta de código. El
lib
directorio ya estarásys.path
allí, por lo que no es necesario agregarlo. Importamos desdefileA
, etc.Si se ejecuta dentro del
lib
directorio comopython foo.py
, el comportamiento es el mismo que para el caso 2.Si se ejecuta dentro del
lib
directorio comopython -m foo
, el comportamiento es similar a los casos 2 y 3. Sin embargo, la ruta allib
directorio no se encuentrasys.path
, por lo que la agregamos antes de importar. Lo mismo se aplica si ejecutamos Python y luegoimport foo
.(Ya que
.
está adentrosys.path
, no necesitamos agregar la versión absoluta de la ruta aquí. Aquí es donde una estructura de anidación de paquetes más profunda, donde queremos hacerfrom ..otherlib.fileC import ...
, hace la diferencia. Si no está haciendo esto, puede omita toda lasys.path
manipulación por completo.)Notas
Todavía hay una peculiaridad. Si ejecuta todo esto desde afuera:
o:
El comportamiento depende de los contenidos de
lib/__init__.py
. Si eso existe y está vacío , todo está bien:Pero si
lib/__init__.py
misma Las importaciones deroutine
modo que pueda exportarroutine.name
directamente comolib.name
, se obtiene:Es decir, el módulo se importa dos veces, una a través del paquete y luego nuevamente
__main__
para que ejecute sumain
código. Python 3.6 y posterior advierten sobre esto:La advertencia es nueva, pero el comportamiento advertido no lo es. Es parte de lo que algunos llaman la trampa de doble importación . (Para detalles adicionales, vea el número 27487 ). Nick Coghlan dice:
Tenga en cuenta que si bien violamos esa regla aquí, lo hacemos solo cuando el archivo que se carga no se carga como parte de un paquete, y nuestra modificación está específicamente diseñada para permitirnos acceder a otros archivos en ese paquete. (Y, como señalé, probablemente no deberíamos hacer esto para paquetes de un solo nivel). Si quisiéramos ser extra limpios, podríamos reescribir esto como, por ejemplo:
Es decir, modificamos el
sys.path
tiempo suficiente para lograr nuestras importaciones, luego lo volvemos a poner como estaba (eliminando una copia de_i
if y solo si agregamos una copia de_i
).fuente
Entonces, después de analizar esto junto con muchos otros, me encontré con una nota publicada por Dorian B en este artículo que resolvió el problema específico que tenía cuando desarrollaba módulos y clases para usar con un servicio web, pero también quiero estar capaz de probarlos mientras estoy codificando, utilizando las instalaciones de depuración en PyCharm. Para ejecutar pruebas en una clase autónoma, incluiría lo siguiente al final de mi archivo de clase:
pero si quisiera importar otras clases o módulos en la misma carpeta, tendría que cambiar todas mis declaraciones de importación de notación relativa a referencias locales (es decir, eliminar el punto (.)) Pero después de leer la sugerencia de Dorian, probé su ' one-liner 'y funcionó! ¡Ahora puedo probar en PyCharm y dejar mi código de prueba en su lugar cuando uso la clase en otra clase bajo prueba, o cuando lo uso en mi servicio web!
La instrucción if verifica si estamos ejecutando este módulo como main o si se está utilizando en otro módulo que se está probando como main . Tal vez esto sea obvio, pero ofrezco esta nota aquí en caso de que alguien más frustrado por los problemas de importación relativos anteriores pueda utilizarlo.
fuente
Aquí hay una solución que no recomendaría, pero podría ser útil en algunas situaciones en las que los módulos simplemente no se generaron:
fuente
Tuve un problema similar en el que no quería cambiar la ruta de búsqueda del módulo Python y necesitaba cargar un módulo relativamente desde un script (a pesar de que "los scripts no pueden importar en relación con todos", como BrenBarn explicó muy bien anteriormente).
Así que usé el siguiente truco. Desafortunadamente, depende del
imp
módulo que quedó en desuso desde la versión 3.4 para descartarse a favorimportlib
. (¿Esto también es posibleimportlib
? No lo sé). Aún así, el truco funciona por ahora.Ejemplo para acceder a miembros de
moduleX
insubpackage1
desde un script que reside en lasubpackage2
carpeta:Un enfoque más limpio parece ser modificar el sys.path utilizado para cargar módulos como lo menciona Federico.
fuente
__name__
cambia dependiendo de si el código en cuestión se ejecuta en el espacio de nombres global o como parte de un módulo importado.Si el código no se ejecuta en el espacio global,
__name__
será el nombre del módulo. Si se está ejecutando en un espacio de nombres global, por ejemplo, si lo escribe en una consola o ejecuta el módulo como un script,python.exe yourscriptnamehere.py
entonces se__name__
convierte en"__main__"
.Verá que
if __name__ == '__main__'
se usa una gran cantidad de código python para probar si el código se ejecuta desde el espacio de nombres global, lo que le permite tener un módulo que también funciona como un script.¿Intentaste hacer estas importaciones desde la consola?
fuente
La respuesta de @ BrenBarn lo dice todo, pero si eres como yo, podría tomar un tiempo entenderlo. Aquí está mi caso y cómo la respuesta de @ BrenBarn se aplica a él, tal vez lo ayude.
El caso
Usando nuestro ejemplo familiar, y agreguemos que moduleX.py tiene una importación relativa a ..moduleA. Dado que intenté escribir un script de prueba en el directorio subpackage1 que importó moduleX, pero luego recibí el temido error descrito por el OP.
Solución
Mueva el script de prueba al mismo nivel que el paquete e importe package.subpackage1.moduleX
Explicación
Como se explicó, las importaciones relativas se realizan en relación con el nombre actual. Cuando mi script de prueba importa moduleX desde el mismo directorio, el nombre del módulo dentro de moduleX es moduleX. Cuando encuentra una importación relativa, el intérprete no puede hacer una copia de seguridad de la jerarquía del paquete porque ya está en la parte superior
Cuando importo moduleX desde arriba, el nombre dentro de moduleX es package.subpackage1.moduleX y se puede encontrar la importación relativa
fuente
Escribió un pequeño paquete de Python para PyPi que podría ayudar a los espectadores de esta pregunta. El paquete actúa como solución alternativa si se desea poder ejecutar archivos de Python que contienen importaciones que contienen paquetes de nivel superior desde un paquete / proyecto sin estar directamente en el directorio del archivo de importación. https://pypi.org/project/import-anywhere/
fuente
Para que Python no regrese a mí "Intento de importación relativa en un paquete no". paquete/
init .py subpackage1 / init .py moduleX.py moduleY.py subpackage2 / init .py moduleZ.py moduleA.py
Este error ocurre solo si está aplicando una importación relativa al archivo principal. Por ejemplo, el archivo padre ya devuelve main después de codificar "print ( name )" en moduleA.py .so ESTE archivo ya es mainno puede devolver ningún paquete principal más adelante. se requieren importaciones relativas en los archivos de paquetes subpackage1 y subpackage2, puede usar ".." para referirse al directorio o módulo padre. Pero el padre es que si ya es un paquete de nivel superior, no puede ir más allá de ese directorio padre (paquete). Dichos archivos donde está aplicando importación relativa a padres solo pueden funcionar con la aplicación de importación absoluta. Si va a utilizar IMPORTACIÓN ABSOLUTA EN EL PAQUETE DE LOS PADRES NO HABRÁ ERROR ya que Python sabe quién está en el nivel superior del paquete, incluso si su archivo está en subpaquetes debido al concepto de RUTA PYTHON que define el nivel superior del proyecto
fuente