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 packagey 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, ..Asube solo un nivel, que todavía está dentro de la packagecarpeta, 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.testrealmente está almacenadopackage(packagees decir, no se considera un paquete). Intentarfrom ..A import fooes 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 pathde 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 pathy dejar que Python resuelva dónde está eso con$PATHy$PYTHONPATH.Cuando usa
python -m package.test_A.test, luego usafrom ..A import fooresuelve muy bien porque realiza un seguimiento de lo que haypackagey 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
-mindicador y ejecutarlo desde el directorio anterior.sys.pathpirateo, 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.pyexiste y enfoo.pytifrom .bar import X.Supuesto:
si está en el
packagedirectorioAytest_Ason paquetes separados.Conclusión: las
..Aimportaciones 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.testparece 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?#includeserí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 fooCreo 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
PYTHONPATHosys.pathincluye.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.testque estés ejecutandotest_A/test.pycon__name__ == '__main__'y__file__ == '/absolute/path/to/test_A/test.py'Eso significa que
test.pypodría usar su absolutoimportsemi-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__.pycarpeta superior, puede inicializar la importación comoimport file/path as aliasen 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.pylofrom ..A import xxxque hacer no escapa del ancla (todavía dentro de lapackagecarpeta), 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.testtest.__name__=test_A.testEntonces, para CASE2, un
from .. import xxxdará como resultado un nuevo módulo con FQMN =package.xxx, lo cual es aceptable.Mientras que para CASE1,
..desde adentrofrom .. import xxxsaltará 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
-tEntonces, 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_classsin dramas importantes.fuente