Parece que ya hay algunas preguntas sobre la importación relativa en Python 3, pero después de analizar muchas de ellas, todavía no encontré la respuesta a mi problema. Así que aquí está la cuestión.
Tengo un paquete que se muestra a continuación
package/
__init__.py
A/
__init__.py
foo.py
test_A/
__init__.py
test.py
y tengo una sola línea en test.py:
from ..A import foo
ahora estoy en la carpeta de package
y ejecuto
python -m test_A.test
Recibí un mensaje
"ValueError: attempted relative import beyond top-level package"
pero si estoy en la carpeta principal de package
, por ejemplo, ejecuto:
cd ..
python -m package.test_A.test
todo esta bien.
Ahora mi pregunta es:
cuando estoy en la carpeta de package
, y ejecuto el módulo dentro del subpaquete test_A ya que test_A.test
, según mi entendimiento, ..A
sube solo un nivel, que todavía está dentro de la package
carpeta, por qué dice el mensaje beyond top-level package
. ¿Cuál es exactamente la razón que causa este mensaje de error?
Respuestas:
EDITAR: Hay respuestas mejores / más coherentes a esta pregunta en otras preguntas:
¿Por qué no funciona? Es porque python no registra desde dónde se cargó un paquete. Entonces, cuando lo hace
python -m test_A.test
, básicamente solo descarta el conocimiento quetest_A.test
realmente está almacenadopackage
(package
es decir, no se considera un paquete). Intentarfrom ..A import foo
es acceder a información que ya no tiene (es decir, directorios hermanos de una ubicación cargada). Es conceptualmente similar a permitir la entradafrom ..os import path
de un archivomath
. Esto sería malo porque desea que los paquetes sean distintos. Si necesitan usar algo de otro paquete, deberían referirse a ellos globalmente confrom os import path
y dejar que Python resuelva dónde está eso con$PATH
y$PYTHONPATH
.Cuando usa
python -m package.test_A.test
, luego usafrom ..A import foo
resuelve muy bien porque realiza un seguimiento de lo que haypackage
y solo está accediendo a un directorio secundario de una ubicación cargada.¿Por qué Python no considera que el directorio de trabajo actual sea un paquete? SIN PISTA , pero Dios mío, sería útil.
fuente
-m
indicador y ejecutarlo desde el directorio anterior.sys.path
pirateo, sino el uso de herramientas de configuración , que es mucho más interesante en mi opinión.Prueba esto. Trabajó para mi.
fuente
A/bar.py
existe y enfoo.py
tifrom .bar import X
.Supuesto:
si está en el
package
directorioA
ytest_A
son paquetes separados.Conclusión: las
..A
importaciones solo se permiten dentro de un paquete.Notas adicionales:
Hacer que las importaciones relativas solo estén disponibles dentro de los paquetes es útil si desea forzar que los paquetes se puedan colocar en cualquier ruta ubicada en
sys.path
.EDITAR:
El directorio de trabajo actual generalmente se encuentra en sys.path. Entonces, todos los archivos allí son importables. Este es el comportamiento desde Python 2 cuando los paquetes aún no existían. Hacer que el directorio en ejecución sea un paquete permitiría importar módulos como "importar .A" y como "importar A", que serían dos módulos diferentes. Tal vez esto sea una inconsistencia a considerar.
fuente
python -m package.test_A.test
parece hacer lo que se desea, y mi argumento es que ese debería ser el valor predeterminado. Entonces, ¿puedes darme un ejemplo de esta inconsistencia?#include
sería muy útil!Ninguna de estas soluciones funcionó para mí en 3.6, con una estructura de carpetas como:
Mi objetivo era importar del módulo1 al módulo2. Lo que finalmente funcionó para mí fue, curiosamente:
Tenga en cuenta el punto único en lugar de las soluciones de dos puntos mencionadas hasta ahora.
Editar: Lo siguiente me ayudó a aclarar esto:
En mi caso, el directorio de trabajo fue (inesperadamente) la raíz del proyecto.
fuente
sys.path.append(".")
funcionó porque lo está llamando en el directorio principal, tenga en cuenta que.
siempre representa el directorio donde ejecuta el comando python.from package.A import foo
Creo que es más claro que
fuente
sys.path.append("..")
. probado en python 3.6Como sugiere la respuesta más popular, básicamente es porque tu
PYTHONPATH
osys.path
incluye.
pero no tu ruta a tu paquete. Y la importación relativa es relativa a su directorio de trabajo actual, no el archivo donde ocurre la importación; extrañamente.Puede solucionar esto cambiando primero su importación relativa a absoluta y luego comenzando con:
O forzar la ruta de Python cuando se llama de esta manera, porque:
Con
python -m test_A.test
que estés ejecutandotest_A/test.py
con__name__ == '__main__'
y__file__ == '/absolute/path/to/test_A/test.py'
Eso significa que
test.py
podría usar su absolutoimport
semi-protegido en la condición de caso principal y también hacer una manipulación de ruta Python única:fuente
Editar: 2020-05-08: Parece que el sitio web que cité ya no está controlado por la persona que escribió el consejo, por lo que estoy eliminando el enlace al sitio. Gracias por dejarme saber baxx.
Si alguien todavía está luchando un poco después de las excelentes respuestas ya proporcionadas, encontré consejos en un sitio web que ya no está disponible.
Cita esencial del sitio que mencioné:
Es bastante obvio que tiene que ser así, pensando en ello después del hecho. Intenté usar sys.path.append ('..') en mis pruebas, pero me encontré con el problema publicado por OP. Al agregar la definición de importación y sys.path antes de mis otras importaciones, pude resolver el problema.
fuente
si tiene una
__init__.py
carpeta superior, puede inicializar la importación comoimport file/path as alias
en ese archivo de inicio Luego puede usarlo en scripts inferiores como:fuente
En mi humilde opinión, entiendo esta pregunta de esta manera:
[CASO 1] Cuando comienzas una importación absoluta como
o
o
en realidad está configurando el ancla de importación para que sea
test_A
, en otras palabras, el paquete de nivel superior estest_A
. Entonces, cuando tenemos test.py dofrom ..A import xxx
, estás escapando del ancla, y Python no permite esto.[CASO 2] Cuando lo haces
o
su ancla se convierte
package
, porpackage/test_A/test.py
lofrom ..A import xxx
que hacer no escapa del ancla (todavía dentro de lapackage
carpeta), y Python felizmente acepta esto.En breve:
Además, podemos usar el nombre de módulo completo (FQMN) para inspeccionar este problema.
Verifique FQMN en cada caso:
test.__name__
=package.test_A.test
test.__name__
=test_A.test
Entonces, para CASE2, un
from .. import xxx
dará como resultado un nuevo módulo con FQMN =package.xxx
, lo cual es aceptable.Mientras que para CASE1,
..
desde adentrofrom .. import xxx
saltará fuera del nodo de inicio (ancla) detest_A
, y Python NO lo permite.fuente
No estoy seguro en Python 2.x, pero en Python 3.6, suponiendo que está intentando ejecutar toda la suite, solo tiene que usar
-t
Entonces, en una estructura como
Por ejemplo, se podría usar:
python3 unittest discover -s /full_path/project_root/tests -t /full_path/project_root/
Y todavía importa el
my_module.my_class
sin dramas importantes.fuente