Importaciones relativas por millonésima vez

717

Yo he estado aquí:

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 .

Paul Roub
fuente
55
¿Cómo estás intentando usar los archivos que muestras? ¿Cuál es el código que estás ejecutando?
BrenBarn
Ver python.org/dev/peps/pep-0328 . Usé el formato de paquete que describí en mi publicación. Los archivos init .py están vacíos. moduleY.py tiene def spam(): pass, moduleA.py tiene def eggs(): pass. Traté de ejecutar un par de comandos "desde .algo importar algo", pero no funcionaron. Nuevamente, vea pep-0328.
66
Mira mi respuesta. Todavía no ha aclarado completamente lo que está haciendo, pero si está tratando de hacerlo from .something import somethingen el intérprete interactivo, eso no funcionará. Las importaciones relativas solo se pueden usar dentro de los módulos, no de forma interactiva.
BrenBarn
105
El mero hecho de que "miles de millones" de personas - ok 83,136 a partir de este comentario - están teniendo suficientes dificultades con las importaciones para buscar esta pregunta; solo podemos concluir que las importaciones de python son contra intuitivas para muchos, si no para la mayoría de los programadores. Guido, tal vez deberías aceptar esto y pedir un comité para rediseñar el mecanismo de importación. Como mínimo, esta sintaxis debería funcionar si x.py y z.py están en el mismo directorio. Es decir, si x.py tiene la declaración, "desde .z import MyZebraClass" x debería importar z ¡INCLUSO si se ejecuta como main ! ¿Por qué es tan difícil?
Steve L
44
Después de leer gran parte de este hilo, aunque no es una respuesta a la pregunta, "solo usar importaciones absolutas" parece ser la solución ...
CodeJockey

Respuestas:

1043

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.pyen la línea de comando. Se carga como un módulo si lo hace python -m myfile, o si se carga cuando importse 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 moduleAdesde 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/subpackage1y luego lo hace import moduleX, el nombre de moduleXsimplemente 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 pythony 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 ..moduleAsignificaría package.moduleA. Para from .. importque funcione, el nombre del módulo debe tener al menos tantos puntos como haya en la importdeclaració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 .. importdeclaraciones 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 moduleXo 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:

  1. Si realmente desea ejecutar moduleXdirectamente, pero aún desea que se considere parte de un paquete, puede hacerlo python -m package.subpackage1.moduleX. Le -mdice a Python que lo cargue como un módulo, no como el script de nivel superior.

  2. 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 packagedirectorio, y ejecútelo. Si por dentro myfile.pyhaces cosas como from package.moduleA import spam, funcionará bien.

Notas

  • Para cualquiera de estas soluciones, el directorio del paquete ( packageen 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).

BrenBarn
fuente
62
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?
77
@Stopforgettingmyaccounts ...: PEP 366 muestra cómo funciona eso. Dentro de un archivo, puede hacer __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í.
BrenBarn
109
Esta debería ser la respuesta a todas las preguntas de importación relativas a Python. Esto debería estar en los documentos, incluso.
edsioufi
10
Consulte python.org/dev/peps/pep-0366 - "Tenga en cuenta que esta repetitiva es suficiente solo si el paquete de nivel superior ya es accesible a través de sys.path. Se necesitaría un código adicional que manipule sys.path para la ejecución directa para trabajar sin que el paquete de nivel superior ya sea importable ". - Esta es la parte más inquietante para mí, ya que este "código adicional" es bastante largo y no puede almacenarse en otro lugar del paquete para ejecutarse fácilmente.
Michael Scott Cuthbert
14
Esta respuesta está actualmente desactivada en algunos detalles importantes sobre __name__y sys.path. Específicamente, con python -m pkg.mod, __name__se establece en __main__, no pkg.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 actual sys.pathcuando se ejecuta python path/to/script.py; agrega el directorio actual sys.pathcuando se ejecuta en la mayoría de los otros modos, incluido python -m pkg.mod.
user2357112 es compatible con Monica el
42

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 :

from .. import foo

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:

if __package__ is None or __package__ == '':
    # uses current directory visibility
    import foo
else:
    # uses current package visibility
    from . import foo
Rami Ka.
fuente
66
La solución correcta es from __future__ import absolute_importy obligar al usuario a usar su código correctamente ... para que siempre pueda hacerlofrom . import foo
Giacomo Alzetta
@Giacomo: la respuesta absolutamente correcta para mi problema. ¡Gracias!
Fábio
8

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.fooy digamos que necesita acceso lib.fileApara las funciones f1y f2, y lib.fileBpara la clase Class3.

He incluido algunas printllamadas para ayudar a ilustrar cómo funciona esto. En la práctica, querrás eliminarlos (y quizás también la from __future__ import print_functionlí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 el if _i in sys.pathprueba. Sin embargo, si cada archivo importado se inserta el mismo camino; por ejemplo, si ambos fileAy fileBquieren utilidades de importación de los paquetes-este estorba hasta sys.pathcon el mismo camino muchas veces, por lo que es bueno tener el if _i not in sys.pathen el texto modelo.

from __future__ import print_function # only when showing how this works

if __package__:
    print('Package named {!r}; __name__ is {!r}'.format(__package__, __name__))
    from .fileA import f1, f2
    from .fileB import Class3
else:
    print('Not a package; __name__ is {!r}'.format(__name__))
    # these next steps should be used only with care and if needed
    # (remove the sys.path manipulation for simple cases!)
    import os, sys
    _i = os.path.dirname(os.path.abspath(__file__))
    if _i not in sys.path:
        print('inserting {!r} into sys.path'.format(_i))
        sys.path.insert(0, _i)
    else:
        print('{!r} is already in sys.path'.format(_i))
    del _i # clean up global name space

    from fileA import f1, f2
    from fileB import Class3

... all the code as usual ...

if __name__ == '__main__':
    import doctest, sys
    ret = doctest.testmod()
    sys.exit(0 if ret.failed == 0 else 1)

La idea aquí es esta (y tenga en cuenta que todos estos funcionan de la misma manera en python2.7 y python 3.x):

  1. Si se ejecuta como import libo from lib import foocomo una importación de paquete normal desde el código ordinario, __packagees liby __name__es lib.foo. Tomamos la primera ruta de código, importando desde .fileA, etc.

  2. Si se ejecuta como python lib/foo.py, __package__será None y __name__será __main__.

    Tomamos la segunda ruta de código. El libdirectorio ya estará sys.pathallí, por lo que no es necesario agregarlo. Importamos desde fileA, etc.

  3. Si se ejecuta dentro del libdirectorio como python foo.py, el comportamiento es el mismo que para el caso 2.

  4. Si se ejecuta dentro del libdirectorio como python -m foo, el comportamiento es similar a los casos 2 y 3. Sin embargo, la ruta al libdirectorio no se encuentra sys.path, por lo que la agregamos antes de importar. Lo mismo se aplica si ejecutamos Python y luego import foo.

    (Ya que . está adentro sys.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 hacer from ..otherlib.fileC import ..., hace la diferencia. Si no está haciendo esto, puede omita toda la sys.pathmanipulación por completo.)

Notas

Todavía hay una peculiaridad. Si ejecuta todo esto desde afuera:

$ python2 lib.foo

o:

$ python3 lib.foo

El comportamiento depende de los contenidos de lib/__init__.py. Si eso existe y está vacío , todo está bien:

Package named 'lib'; __name__ is '__main__'

Pero si lib/__init__.py misma Las importaciones de routinemodo que pueda exportar routine.namedirectamente como lib.name, se obtiene:

$ python2 lib.foo
Package named 'lib'; __name__ is 'lib.foo'
Package named 'lib'; __name__ is '__main__'

Es decir, el módulo se importa dos veces, una a través del paquete y luego nuevamente __main__para que ejecute su maincódigo. Python 3.6 y posterior advierten sobre esto:

$ python3 lib.routine
Package named 'lib'; __name__ is 'lib.foo'
[...]/runpy.py:125: RuntimeWarning: 'lib.foo' found in sys.modules
after import of package 'lib', but prior to execution of 'lib.foo';
this may result in unpredictable behaviour
  warn(RuntimeWarning(msg))
Package named 'lib'; __name__ is '__main__'

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:

Esta próxima trampa existe en todas las versiones actuales de Python, incluida la 3.3, y se puede resumir en la siguiente directriz general: "Nunca agregue un directorio de paquete, o cualquier directorio dentro de un paquete, directamente a la ruta de Python".

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:

    import os, sys
    _i = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    if _i not in sys.path:
        sys.path.insert(0, _i)
    else:
        _i = None

    from sub.fileA import f1, f2
    from sub.fileB import Class3

    if _i:
        sys.path.remove(_i)
    del _i

Es decir, modificamos el sys.pathtiempo suficiente para lograr nuestras importaciones, luego lo volvemos a poner como estaba (eliminando una copia de _iif y solo si agregamos una copia de _i).

torek
fuente
7

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:

if __name__ == '__main__':
   # run test code here...

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!

# import any site-lib modules first, then...
import sys
parent_module = sys.modules['.'.join(__name__.split('.')[:-1]) or '__main__']
if __name__ == '__main__' or parent_module.__name__ == '__main__':
    from codex import Codex # these are in same folder as module under test!
    from dblogger import DbLogger
else:
    from .codex import Codex
    from .dblogger import DbLogger

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.

Steve L
fuente
1
Eso realmente lo resuelve. Pero es realmente desagradable. ¿Por qué no es este el comportamiento predeterminado?
lo tolmencre
4

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:

import os
import sys
parent_dir_name = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
sys.path.append(parent_dir_name + "/your_dir")
import your_script
your_script.a_function()
Federico
fuente
2

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 impmódulo que quedó en desuso desde la versión 3.4 para descartarse a favor importlib. (¿Esto también es posible importlib? No lo sé). Aún así, el truco funciona por ahora.

Ejemplo para acceder a miembros de moduleXin subpackage1desde un script que reside en la subpackage2carpeta:

#!/usr/bin/env python3

import inspect
import imp
import os

def get_script_dir(follow_symlinks=True):
    """
    Return directory of code defining this very function.
    Should work from a module as well as from a script.
    """
    script_path = inspect.getabsfile(get_script_dir)
    if follow_symlinks:
        script_path = os.path.realpath(script_path)
    return os.path.dirname(script_path)

# loading the module (hack, relying on deprecated imp-module)
PARENT_PATH = os.path.dirname(get_script_dir())
(x_file, x_path, x_desc) = imp.find_module('moduleX', [PARENT_PATH+'/'+'subpackage1'])
module_x = imp.load_module('subpackage1.moduleX', x_file, x_path, x_desc)

# importing a function and a value
function = module_x.my_function
VALUE = module_x.MY_CONST

Un enfoque más limpio parece ser modificar el sys.path utilizado para cargar módulos como lo menciona Federico.

#!/usr/bin/env python3

if __name__ == '__main__' and __package__ is None:
    from os import sys, path
    # __file__ should be defined in this case
    PARENT_DIR = path.dirname(path.dirname(path.abspath(__file__)))
   sys.path.append(PARENT_DIR)
from subpackage1.moduleX import *
Lars
fuente
Eso se ve mejor ... lástima que todavía requiera que incruste el nombre del directorio principal en el archivo ... tal vez eso pueda mejorarse con importlib. Tal vez importlib puede incluso ser parcheado para que la importación relativa "simplemente funcione" para casos de uso simples. Voy a echarle un vistazo.
Andrew Wagner
Sin embargo, estoy usando Python 2.7.14. ¿Algo como esto todavía funcionaría?
user3474042
Acabo de probar ambos enfoques en Python 2.7.10 y funcionaron bien para mí. De hecho, no tiene el problema de un módulo imp obsoleto en 2.7, así que mucho mejor.
Lars
2

__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.pyentonces 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?

teodoxo
fuente
Ah, entonces mencionas -m. Eso hace que su módulo se ejecute como un script: si pega un if __name__ == '__main__' allí, debería ver que es '__main__' debido a -m. Intente simplemente importar su módulo a otro módulo para que no sea el nivel superior ... lo que debería permitirle realizar la importación relativa
theodox el
Intenté hacer estas importaciones desde la consola, con el archivo activo como el módulo correcto.
@Stopforgettingmyaccounts ...: ¿Qué quieres decir con el "archivo activo"?
BrenBarn
Yo uso Pyscripter. Estaba en moduleX.py cuando ejecuté estas importaciones: desde .moduleY import spam y desde. Módulo de importación
¿No importar .moduleY seguido de moduleY.spam ()?
Theodox
2

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

package/
    __init__.py
    subpackage1/
        __init__.py
        moduleX.py
    moduleA.py

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

Brad Dre
fuente
Esperando que puedas guiarme en esto. En el siguiente enlace, si va al Caso 3, dice que la solución 1 no es posible. Por favor, ¿puede verificar esto y hágamelo saber? Me ayudará inmensamente. chrisyeh96.github.io/2017/08/08/…
variable
@variable hay un error tipográfico en el enlace y no puedo editar. Observé el caso 3 y no seguí exactamente a lo que nos referíamos. Cuando probé ese ejemplo en python 2 no hubo problemas que me hagan pensar que me perdí algo. Tal vez debería publicar una nueva pregunta, pero debe proporcionar un ejemplo más claro. El caso 4 toca lo que estoy hablando en mi respuesta aquí: no puede subir un directorio para importación relativa A MENOS que el intérprete comience en un directorio principal
Brad Dre
Gracias, me refiero a Python 3 y aquí la pregunta stackoverflow.com/questions/58577767/…
variable
1

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.

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/

ec2604
fuente
-2

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

Sakshi Jain
fuente