Importaciones relativas por milmillonésima vez

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 siempre recurrente es esta: ¿cómo resuelvo este mensaje de "Intento de importación relativa en un paquete que no es"?

ImportError: attempted relative import with no known parent package

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 hicieron desde la consola.

Hice 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í son todos 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 del paquete. 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 para mí todo son jeroglíficos. Entonces, mi pregunta, ¿cómo hago para que Python no me devuelva "Intento de importación relativa sin paquete"? tiene una respuesta que implica -m, supuestamente.

¿Puede alguien decirme por qué Python da ese mensaje de error, qué significa "no paquete", por qué y cómo se define un "paquete" y la respuesta precisa expresada en términos lo suficientemente fáciles de entender para un niño de jardín de infantes.

preguntado el 03 de enero de 13 a las 03:01

¿Cómo intentas usar los archivos que muestras? ¿Cuál es el código que estás ejecutando? -

Mira mi respuesta. Todavía no has aclarado completamente lo que estás haciendo, pero si estás tratando de hacer 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. -

El mero hecho de que "miles de millones" de personas (bueno, 83,136 XNUMX 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 contrarias a la intuición 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, "from .z import MyZebraClass" x debería importar z INCLUSO si se está ejecutando como principal! ¿Por qué es tan difícil? -

Después de leer gran parte de este hilo, aunque no es una respuesta a la pregunta, "simplemente use importaciones absolutas" parece ser la solución ... -

12 Respuestas

Guión frente a 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 simple hecho de saber en qué directorio se encuentra un archivo no determina en qué paquete piensa Python que se encuentra. Eso depende, además, de cómo cargue el archivo en Python (ejecutándolo o importándolo).

Hay dos formas de cargar un archivo de Python: como script de nivel superior o como 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 cuando un import declaración se encuentra dentro de algún 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 tu ejemplo:

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

si importaste moduleX (Nota: importado, no ejecutada directamente), su nombre sería package.subpackage1.moduleX. Si importaste moduleA, su nombre seria package.moduleA. Sin embargo, si usted ejecutar directamente moduleX desde la línea de comando, su nombre será en su lugar __main__, y si ejecuta directamente moduleA desde la línea de comando, su nombre será __main__. Cuando un módulo se ejecuta como el 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 un problema adicional: el nombre del módulo depende de si se importó "directamente" desde el directorio en el que se encuentra o si se importó a través de un paquete. Esto solo hace una 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 haz import moduleX, el nombre de moduleX solo será moduleX, y no package.subpackage1.moduleX. Esto se debe a que Python agrega el directorio actual a su ruta de búsqueda cuando se ingresa al intérprete de forma interactiva; 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 forma interactiva (por ejemplo, simplemente escriba python y comience a ingresar el código de 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. Todo lo 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 del paquete. 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 módulo nombre para determinar dónde está en un paquete. Cuando usas una importación relativa como from .. import foo, los puntos indican que se debe subir una cierta cantidad de niveles en la jerarquía del paquete. Por ejemplo, si el nombre de su módulo actual es package.subpackage1.moduleX, entonces ..moduleA Significaría package.moduleA. Para from .. import para funcionar, el nombre del módulo debe tener al menos tantos puntos como hay en el import .

... son solo 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 en su interior. Si intenta hacerlo, obtendrá el error "importación relativa en no paquete".

Los scripts no pueden importar relativos

Lo que probablemente hiciste es que intentaste correr moduleX o similar desde la línea de comando. 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 se encuentra 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 siempre es __main__. Así no puede hacer 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 quieres correr moduleX directamente, pero aún desea que se considere parte de un paquete, puede hacerlo python -m package.subpackage1.moduleX. La -m le dice a Python que lo cargue como un módulo, no como el script de nivel superior.

  2. O tal vez en realidad no quieres corrida moduleX, solo desea ejecutar otro script, digamos myfile.py, Que usos funciones dentro moduleX. Si ese es el caso, ponga myfile.py en algún otro lugarno dentro de package directorio – y ejecútelo. si 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 de Python (sys.path). Si no es así, no podrá usar nada en el paquete de manera confiable.

  • Desde Python 2.6, el "nombre" del módulo para fines de resolución de paquetes está determinado no solo por su __name__ atributos sino también por la __package__ atributo. Por eso estoy evitando usar el símbolo explícito __name__ para referirse al "nombre" del módulo. Desde Python 2.6, el "nombre" de un módulo es efectivamente __package__ + '.' + __name__, O simplemente __name__ if __package__ is None.)

Respondido 22 Oct 21, 12:10

Esta debería ser la respuesta a todas las preguntas sobre importaciones relativas de Python. Esto debería estar en los documentos, incluso. - edsioufi

Ver python.org/dev/peps/pep-0366 -- "Tenga en cuenta que este modelo es suficiente solo si ya se puede acceder al paquete de nivel superior a través de sys.path . Se necesitaría un código adicional que manipule sys.path para que la ejecución directa funcione sin que el paquete de nivel superior ya sea importable". -- esta es la parte más preocupante para mí, ya que este "código adicional" en realidad es bastante largo y no se puede almacenar en otro lugar del paquete para que se ejecute fácilmente. - Michael Scott Asato Cuthbert

Sigo volviendo a esta publicación a pesar de ser un veterano de Python. El mensaje principal para mí es: O juegas con sys.path y __package__ (que es bastante feo, vea las otras respuestas) o simplemente cree un "script principal" main.py en el directorio raíz de su proyecto y coloque todos los módulos a importar en subdirectorios. main.py luego puede acceder a todos los módulos directamente a través de los nombres de sus paquetes (= los nombres de las respectivas carpetas en las que se encuentran). - balu

Esta respuesta está actualmente desactivada en algunos detalles importantes con respecto a __name__ y sys.path. Específicamente, con python -m pkg.mod, __name__ se establece a __main__no, pkg.mod; las importaciones relativas se resuelven usando __package__ más bien que __name__ en este caso. Además, Python agrega el directorio del script en lugar del directorio actual para sys.path al correr python path/to/script.py; agrega el directorio actual a sys.path cuando se ejecuta la mayoría de las otras formas, incluyendo python -m pkg.mod. - user2357112

Finalmente entiendo después de horas de lectura... Vale la pena señalar que el código debajo if __name__ == '__main__' aún se ejecutará al usar -m. Vea el comentario de @ user2357112 - AdamF

Esto es realmente un problema dentro de python. El origen de la confusión es que la gente erróneamente toma la importancia relativa como un camino relativo que no lo es.

Por ejemplo, cuando escribes en faa.py:

from .. import foo

Esto tiene un significado sólo si faa.py fue identificado y cargado por python, durante la ejecución, como parte de un paquete. En ese caso, el nombre del modulo en faa.py sería por ejemplo some_packagename.faa. Si el archivo se cargó simplemente porque está en el directorio actual, cuando se ejecuta python, entonces su nombre no se referiría a ningún paquete y eventualmente fallaría la importación relativa.

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

Respondido el 14 de diciembre de 18 a las 21:12

La solucion correcta es from __future__ import absolute_import y obligue al usuario a usar su código correctamente... para que siempre pueda hacer from . import foo - Giacomo Alzetta

Entonces, después de quejarme de esto junto con muchos otros, encontré una nota publicada por Dorian B. en este artículo eso resolvió el problema específico que tenía donde desarrollaría módulos y clases para usar con un servicio web, pero también quiero poder probarlos mientras estoy codificando, usando las funciones del depurador en PyCharm. Para ejecutar pruebas en una clase independiente, 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 la 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 declaración if verifica si estamos ejecutando este módulo como principal o si se está utilizando en otro módulo que se está probando como principal. Tal vez esto sea obvio, pero ofrezco esta nota aquí en caso de que alguien más frustrado por los problemas de importación relativa anteriores pueda hacer uso de ella.

Respondido 05 Oct 18, 02:10

Eso en realidad lo resuelve. Pero es realmente desagradable. ¿Por qué este no es el comportamiento predeterminado? - lo tolmencre

Tengo un problema similar: herramientas que deben empaquetarse en la misma carpeta para que funcionen como un complemento de otro programa más grande. El complemento principal interactúa con el programa más grande y funciona solo cuando ese programa más grande se está ejecutando. Para las pruebas, quiero ejecutar las utilidades más pequeñas y dejar que se llamen entre sí. Es una pesadilla. Empecé a usar encadenado try/except ImportError bloques y agregando todas las formas posibles de importar algo allí. Funciona, es corto, pero es tan increíblemente poco pitónico que siempre duele. - Murciélago marrón

Este es mi caso de uso exacto, prueba/depuración dentro de PyCharm. La solución, para los usuarios de PyCharm, es configurar una o más 'Raíces de origen'. De los documentos de PyCharm "PyCharm usa las raíces de origen como punto de partida para resolver las importaciones". - jetbrains.com/help/pycharm/configuring-project-structure.html - Brian Wylie

Dijiste que usas Pycharm. Gestiona las importaciones por sí mismo, y tienes que copiarlas manualmente cada vez. No es una buena solución. - smit johnth

"Tal vez esto es obvio" .. umm esta el código es obvio? En cualquier caso, lo guardaré en algún lugar, dado que vivo y muero con las herramientas de JetBrains... - WestCoastProyectos

Hay demasiadas respuestas demasiado largas en un idioma extranjero. Así que intentaré hacerlo breve.

Si tú escribes from . import module, contrario a lo que piensas, module no se importará desde el directorio actual, sino desde el nivel superior de su paquete. Si ejecuta el archivo .py como un script, simplemente no sabe dónde está el nivel superior y, por lo tanto, se niega a funcionar.

Si lo empiezas así py -m package.module del directorio de arriba package, Python sabe dónde está el nivel superior. Eso es muy similar a Java: java -cp bin_directory package.class

contestado el 24 de mayo de 22 a las 00:05

Esto está en la respuesta de @ BrenBarn, pero es el TL; DR. OP y cualquier otra persona que busque respuestas, esto es todo. Me tomó una eternidad encontrar esto en otro lugar. - levi lesches

Lo que es aún más confuso es que cuando instala un paquete, las importaciones absolutas no funcionan para mí. necesito usar desde .submodule import module. Cuando uso import submodule.module or from submodule import module, no se puede encontrar, incluso cuando la carpeta está justo en la carpeta del paquete. - espartano

Aquí hay una receta general, modificada para que encaje como ejemplo, que estoy usando en este momento para tratar con bibliotecas de Python escritas como paquetes, que contienen archivos interdependientes, donde quiero poder probar partes de ellos por partes. llamemos a esto lib.foo y decir que necesita acceso a lib.fileA para funciones f1 y f2 y lib.fileB para clase Class3.

He incluido algunos print llamadas para ayudar a ilustrar cómo funciona esto. En la práctica, le gustaría eliminarlos (y tal vez también el from __future__ import print_function línea).

Este ejemplo en particular es demasiado simple para mostrar cuando realmente necesitamos insertar una entrada en sys.path. (Ver la respuesta de lars para un caso en el que nosotros do lo necesitamos, cuando tenemos dos o más niveles de directorios de paquetes, y luego usamos os.path.dirname(os.path.dirname(__file__))—pero en realidad no daño aquí tampoco.) También es lo suficientemente seguro para hacer esto sin el if _i in sys.path prueba. Sin embargo, si cada archivo importado inserta la misma ruta, por ejemplo, si ambos fileA y fileB desea importar utilidades del paquete; esto desordena sys.path con el mismo camino muchas veces, por lo que es bueno tener la if _i not in sys.path en el repetitivo.

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 funcionan de la misma manera en python2.7 y python 3.x):

  1. Si se ejecuta como import lib or from lib import foo como un paquete regular importado desde código ordinario, __package is lib y __name__ is lib.foo. Tomamos la primera ruta del código, importando desde .fileA, etc.

  2. Si se ejecuta como python lib/foo.py, __package__ será Ninguno y __name__ se mostrarán __main__.

    Tomamos la segunda ruta del código. los lib el directorio ya estará en sys.path así que no hay necesidad de agregarlo. importamos de fileA, etc.

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

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

    (Ya que . is in sys.path, realmente no necesitamos agregar la versión absoluta de la ruta aquí. Aquí es donde una estructura de anidamiento de paquetes más profunda, donde queremos hacer from ..otherlib.fileC import ..., marca la diferencia. Si no está haciendo esto, puede omitir todos los sys.path manipulación por completo.)

Notas

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

$ python2 lib.foo

o bien:

$ python3 lib.foo

el comportamiento depende del contenido de lib/__init__.py. Si eso existe y esta vacio, todo está bien:

Package named 'lib'; __name__ is '__main__'

Pero si lib/__init__.py sí mismo importaciones routine para que pueda exportar routine.name directamente como lib.name, usted 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 otra vez como __main__ para que corra tu main código. Python 3.6 y versiones posteriores 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 nuevo, pero el comportamiento advertido no lo es. Es parte de lo que algunos llaman la doble trampa de importación. (Para más detalles ver problema 27487.) Nick Coghlan dice:

Esta siguiente trampa existe en todas las versiones actuales de Python, incluida la 3.3, y se puede resumir en la siguiente pauta general: "Nunca agregue un directorio de paquetes, 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 , solamente cuando el archivo que se está cargando es no siendo cargado como parte de un paquete, y nuestra modificación está diseñada específicamente para permitirnos acceder a otros archivos en ese paquete. (Y, como señalé, probablemente no deberíamos hacer esto para los paquetes de un solo nivel). Si quisiéramos ser más 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 sys.path el tiempo suficiente para lograr nuestras importaciones, luego volver a ponerlo como estaba (borrando una copia de _i si y solo si agregamos una copia de _i).

Respondido el 20 de junio de 20 a las 10:06

Aquí hay una solución que no recomendaría, pero que 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()

Respondido el 21 de junio de 16 a las 03:06

La respuesta de @BrenBarn lo dice todo, pero si eres como yo, puede que te lleve un tiempo entenderlo. Aquí está mi caso y cómo se aplica la respuesta de @ BrenBarn, tal vez lo ayude.

El caso

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

Usando nuestro ejemplo familiar, y agréguele que moduleX.py tiene una importación relativa a ..moduleA. Dado que intenté escribir un script de prueba en el directorio subpaquete1 que importó el módulo X, pero luego obtuve el temido error descrito por el OP.

Solución

Mueva el script de prueba al mismo nivel que el paquete e importe el paquete.subpaquete1.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, entonces 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, entonces el nombre dentro de moduleX es package.subpackage1.moduleX y se puede encontrar la importación relativa

Respondido 09 ago 19, 00:08

Espero me puedan orientar en esto. En el siguiente enlace, si va al Caso 3, dice que la solución 1 no es posible. Por favor, puedes comprobar esto y me avisas. Me ayudará inmensamente. chrisyeh96.github.io/2017/08/08/… - variable

@variable hay un error tipográfico en el enlace y no puedo editar. Miró el caso 3 y no siguió exactamente lo que quiere decir. Cuando probé ese ejemplo en python 2 no hubo problemas, lo que me hace pensar que me perdí algo. Tal vez debería publicar una nueva pregunta pero necesita proporcionar un ejemplo más claro. El caso 4 toca lo que estoy hablando en mi respuesta aquí: no puede subir un directorio para una importación relativa A MENOS QUE el intérprete comience en un directorio principal: brad dr

gracias me refiero a python 3 y aqui la pregunta stackoverflow.com/questions/58577767/… - variable

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 de un guión (a pesar de "los scripts no pueden importar en relación con todos" como BrenBarn explicó muy bien arriba).

Entonces usé el siguiente truco. Desafortunadamente, depende de la imp módulo que quedó en desuso desde la versión 3.4 para ser eliminado a favor de importlib. (¿Es esto posible con importlib, ¿también? No lo sé.) Aún así, el truco funciona por ahora.

Ejemplo para acceder a miembros de moduleX in subpackage1 de un script que reside en el subpackage2 carpeta:

#!/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 *

Respondido 19 Jul 16, 11:07

Eso se ve mejor... lástima que aún requiera que incrustes el nombre del directorio principal en el archivo... tal vez eso se pueda mejorar con importlib. Tal vez importlib puede incluso ser parcheado para hacer que la importación relativa "simplemente funcione" para casos de uso simples. Voy a tomar una grieta en él. - Andrew Wagner

Sin embargo, estoy usando Python 2.7.14. ¿Algo como esto todavía funcionaría? - ChumbiChubaGo

Acabo de probar ambos enfoques en python 2.7.10 y me funcionaron bien. De hecho, no tiene el problema de un módulo imp en desuso en 2.7, así que mucho mejor. - Lars

__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 usando python.exe yourscriptnamehere.py luego __name__ se convierte en "__main__".

Verá una gran cantidad de código Python con if __name__ == '__main__' se usa para probar si el código se ejecuta desde el espacio de nombres global, lo que le permite tener un módulo que funciona como un script.

¿Intentaste hacer estas importaciones desde la consola?

Respondido 21 Abr '17, 18:04

Ah, entonces mencionas -m. Eso hace que su módulo se ejecute como una secuencia de comandos: si coloca 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 ... eso debería permitirle hacer la importación relativa: teodoxo

Traté de hacer estas importaciones desde la consola, siendo el archivo activo el módulo correcto. - usuario1881400

@Stopforgettingmyaccounts...: ¿Qué quiere decir con el "archivo activo"? - BrenBarn

Yo uso Pyscripter. Estaba en moduleX.py cuando ejecuté estas importaciones: from .moduleY import spam y de . importar MóduloY. - usuario1881400

¿No importar .moduleY seguido de moduleY.spam()? - teodoxo

Siguiendo con lo que Lars ha sugerido, he envuelto este enfoque en una nueva biblioteca de importación experimental: ultraimportación

Le da al programador más control sobre las importaciones y permite las importaciones basadas en el sistema de archivos. Por lo tanto, puede realizar importaciones relativas desde scripts. Paquete principal no necesario. ultraimports siempre funcionará, sin importar cómo ejecute su código o cuál sea su directorio de trabajo actual porque ultraimport hace que las importaciones sean inequívocas. No necesita cambiar sys.path y tampoco necesita un bloque try/except para hacer importaciones relativas a veces y absolutas a veces.

Entonces escribirías en somefile.py algo como:

import ultraimport
foo = ultraimport('__dir__/foo.py')

__dir__ es el directorio de somefile.py, el llamador de ultraimport(). foo.py viviría en el mismo directorio que somefile.py.

Una advertencia al importar scripts como este es si contienen más importaciones relativas. ultraimport tiene un preprocesador integrado para reescribir importaciones relativas posteriores a ultraimports para que sigan funcionando. Sin embargo, actualmente esto es algo limitado ya que las importaciones originales de Python son ambiguas y no hay mucho que puedas hacer al respecto.

Respondido el 20 de junio de 22 a las 04:06

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 del paquete. 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.

Escribí un pequeño paquete de python para PyPi que podría ayudar a los espectadores de esta pregunta. El paquete actúa como una solución alternativa si se desea poder ejecutar archivos python que contengan importaciones que contengan paquetes de nivel superior desde dentro de un paquete/proyecto sin estar directamente en el directorio del archivo de importación. https://pypi.org/project/import-anywhere/

Respondido 24 Feb 18, 16:02

En la mayoría de los casos, cuando veo el ValueError: attempted relative import beyond top-level package y tirarme de los pelos, la solución es la siguiente:

Tu necesitas paso un nivel más alto en la jerarquía de archivos!

#dir/package/module1/foo.py

#dir/package/module2/bar.py
from ..module1 import foo

Importador bar.py cuando el intérprete se inicia en dir/package/ dará como resultado un error a pesar de que el proceso de importación nunca va más allá de su directorio actual.

Importador bar.py cuando el intérprete se inicia en dir/ podría suceder.

Del mismo modo para las pruebas unitarias: python3 -m unittest discover --start-directory=. funciona con éxito desde dir/, pero no de dir/package/.

Respondido 25 Abr '22, 06:04

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.