Quiero importar una función desde otro archivo en el mismo directorio.
A veces me funciona, from .mymodule import myfunction
pero a veces me sale un:
SystemError: Parent module '' not loaded, cannot perform relative import
A veces funciona from mymodule import myfunction
, pero a veces también obtengo un:
SystemError: Parent module '' not loaded, cannot perform relative import
No entiendo la lógica aquí, y no pude encontrar ninguna explicación. Esto se ve completamente al azar.
¿Podría alguien explicarme cuál es la lógica detrás de todo esto?
python
python-3.x
python-import
John Smith opcional
fuente
fuente
Respuestas:
Es bastante común tener un diseño como este ...
... con algo
mymodule.py
así ......
myothermodule.py
así ...... y
main.py
así ...... que funciona bien cuando ejecuta
main.py
omypackage/mymodule.py
, pero fallamypackage/myothermodule.py
, debido a la importación relativa ...La forma en que se supone que debes ejecutarlo es ...
... pero es algo detallado y no se mezcla bien con una línea shebang como
#!/usr/bin/env python3
.La solución más simple para este caso, suponiendo que el nombre
mymodule
sea globalmente único, sería evitar el uso de importaciones relativas, y simplemente usar ...... aunque, si no es único, o la estructura de su paquete es más compleja, deberá incluir el directorio que contiene el directorio de su paquete
PYTHONPATH
y hacerlo así ...... o si desea que funcione "fuera de la caja", puede extraer el
PYTHONPATH
código primero con esto ...Es un poco doloroso, pero hay una pista de por qué en un correo electrónico escrito por cierto Guido van Rossum ...
Si ejecutar scripts dentro de un paquete es un antipatrón o no es subjetivo, pero personalmente me parece realmente útil en un paquete que tengo que contiene algunos widgets wxPython personalizados, por lo que puedo ejecutar el script para cualquiera de los archivos fuente para mostrar
wx.Frame
solo ese widget para fines de prueba.fuente
os.path.realpath(os.path.dirname(inspect.getfile(inspect.currentframe())))
si estuviera seguro de que su módulo siempre tiene una propiedad adecuadafile
que también podría usaros.path.realpath(os.path.dirname(__file__))
.sys.path.append( os.path.join( os.path.dirname(__file__), os.path.pardir ) )
...which I've always seen as an antipattern.
No veo cómo es un anti patrón ... Parece que sería muy conveniente simplemente hacer que las importaciones relativas funcionen intuitivamente. Solo quiero poder importar cosas que sé que están en el mismo directorio. Me pregunto cuál fue su razonamientoExplicación
De PEP 328
En algún momento, PEP 338 entró en conflicto con PEP 328 :
y para abordar el problema, PEP 366 introdujo la variable de nivel superior
__package__
:(énfasis mío)
Si
__name__
es'__main__'
,__name__.rpartition('.')[0]
devuelve una cadena vacía. Es por eso que hay una cadena literal vacía en la descripción del error:La parte relevante de la
PyImport_ImportModuleLevelObject
función de CPython :CPython genera esta excepción si no pudo encontrar
package
(el nombre del paquete) eninterp->modules
(accesible comosys.modules
). Dado quesys.modules
es "un diccionario que asigna nombres de módulos a módulos que ya se han cargado" , ahora está claro que el módulo principal debe importarse de forma explícita antes de realizar la importación relativa .Nota: El parche del problema 18018 ha agregado otro
if
bloque , que se ejecutará antes del código anterior:Si
package
(igual que el anterior) es una cadena vacía, el mensaje de error seráSin embargo, solo verá esto en Python 3.6 o posterior.
Solución n. ° 1: Ejecute su script usando -m
Considere un directorio (que es un paquete de Python ):
Todos los archivos en el paquete comienzan con las mismas 2 líneas de código:
Incluyo estas dos líneas solo para que el orden de las operaciones sea obvio. Podemos ignorarlos por completo, ya que no afectan la ejecución.
__init__.py y module.py contienen solo esas dos líneas (es decir, están efectivamente vacías).
standalone.py además intenta importar module.py a través de la importación relativa:
Somos conscientes de que
/path/to/python/interpreter package/standalone.py
fallará. Sin embargo, podemos ejecutar el módulo con la-m
opción de línea de comando que "buscarásys.path
el módulo nombrado y ejecutará su contenido como__main__
módulo" :-m
hace todas las cosas de importación por usted y las configura automáticamente__package__
, pero puede hacerlo usted mismo en elSolución n. ° 2: configure __package__ manualmente
Trátelo como una prueba de concepto en lugar de una solución real. No es adecuado para su uso en código del mundo real.
PEP 366 tiene una solución a este problema, sin embargo, está incompleto, porque la configuración por
__package__
sí sola no es suficiente. Deberá importar al menos N paquetes anteriores en la jerarquía del módulo, donde N es el número de directorios principales (en relación con el directorio del script) que se buscarán para el módulo que se importará.Así,
Agregue el directorio padre del enésimo predecesor del módulo actual a
sys.path
Eliminar el directorio del archivo actual de
sys.path
Importe el módulo principal del módulo actual utilizando su nombre completo
Establecer
__package__
en el nombre completo de 2Realizar la importación relativa
Tomaré prestados archivos de la Solución # 1 y agregaré algunos subpaquetes más:
Esta vez standalone.py importará module.py del paquete de paquete utilizando la siguiente relativa de las importaciones
Tendremos que preceder esa línea con el código repetitivo para que funcione.
Nos permite ejecutar standalone.py por nombre de archivo:
Aquí se puede encontrar una solución más general envuelta en una función . Ejemplo de uso:
Solución n. ° 3: use importaciones absolutas y herramientas de configuración
Los pasos son:
Reemplazar importaciones relativas explícitas con importaciones absolutas equivalentes
Instalar
package
para hacerlo importablePor ejemplo, la estructura del directorio puede ser la siguiente
donde setup.py es
El resto de los archivos fueron tomados de la Solución # 1 .
La instalación le permitirá importar el paquete independientemente de su directorio de trabajo (suponiendo que no haya problemas de nombres).
Podemos modificar standalone.py para usar esta ventaja (paso 1):
Cambie su directorio de trabajo
project
y ejecútelo/path/to/python/interpreter setup.py install --user
(--user
instala el paquete en el directorio de paquetes de su sitio ) (paso 2):Verifiquemos que ahora sea posible ejecutar standalone.py como script:
Nota : Si decide seguir esta ruta, sería mejor usar entornos virtuales para instalar paquetes de forma aislada.
Solución # 4: Use importaciones absolutas y algo de código repetitivo
Francamente, la instalación no es necesaria: puede agregar un código repetitivo a su secuencia de comandos para que las importaciones absolutas funcionen.
Voy a tomar prestados archivos de la Solución n. ° 1 y cambiar standalone.py :
Añadir el directorio padre del paquete a
sys.path
antes de intentar importar nada de paquete usando importaciones en términos absolutos:Reemplace la importación relativa por la importación absoluta:
standalone.py se ejecuta sin problemas:
Creo que debería advertirte: trata de no hacer esto, especialmente si tu proyecto tiene una estructura compleja.
Como nota al margen, PEP 8 recomienda el uso de importaciones absolutas, pero establece que en algunos escenarios las importaciones relativas explícitas son aceptables:
fuente
__package__
manualmente si el nombre es__main__
para resolver el problema?imp
módulo y configurarlo en__package__
consecuencia, pero el resultado es claramente un antipatrón.AttributeError: 'PosixPath' object has no attribute 'path'
.Ponga esto dentro del archivo __init__.py de su paquete :
Asumiendo que su paquete es así:
Ahora use importaciones regulares en su paquete, como:
Esto funciona tanto en python 2 como en 3.
fuente
__init__.py
resolverá básicamente todos los errores de importación relativos.sys.path
porque me preocupa que pueda afectar a otro código. (Esto se debe en parte a que no conozco las complejidades de cómo funciona)Me encontré con este problema. Se está importando una solución alternativa de pirateo a través de un bloque if / else como el siguiente:
fuente
except:
es malo. utilizarexcept ImportError:
en su lugar!SystemError
aqui (Py 3.4)if __name__ == '__main__': from mymod import as_int; else: from .mymod import as_int
.Con suerte, esto será de valor para alguien por ahí: revisé media docena de publicaciones de stackoverflow tratando de descubrir importaciones relativas similares a las publicadas aquí arriba. Configuré todo según lo sugerido pero todavía estaba golpeando
ModuleNotFoundError: No module named 'my_module_name'
Como solo estaba desarrollando localmente y jugando, no había creado / ejecutado un
setup.py
archivo. Tampoco aparentemente había configurado miPYTHONPATH
.Me di cuenta de que cuando ejecuté mi código como lo había estado cuando las pruebas estaban en el mismo directorio que el módulo, no pude encontrar mi módulo:
Sin embargo, cuando especifiqué explícitamente la ruta, las cosas comenzaron a funcionar:
Entonces, en el caso de que alguien haya intentado algunas sugerencias, cree que su código está estructurado correctamente y aún se encuentra en una situación similar a la mía, intente cualquiera de los siguientes si no exporta el directorio actual a su PYTHONPATH:
$ PYTHONPATH=. python3 test/my_module/module_test.py
PYTHONPATH=.
, cree unsetup.py
archivo con contenido como el siguiente y ejecútelopython setup.py development
para agregar paquetes a la ruta:fuente
Necesitaba ejecutar python3 desde el directorio principal del proyecto para que funcione.
Por ejemplo, si el proyecto tiene la siguiente estructura:
Solución
Ejecutaría python3 dentro de la carpeta project_demo / y luego realizaría un
fuente
Para evitar este problema, ideé una solución con el paquete de reempaquetado , que me ha funcionado durante algún tiempo. Agrega el directorio superior a la ruta lib:
El reempaquetado puede realizar importaciones relativas que funcionan en una amplia gama de casos, utilizando una estrategia inteligente (inspección de la pila de llamadas).
fuente
si ambos paquetes están en su ruta de importación (sys.path), y el módulo / clase que desea es en example / example.py, para acceder a la clase sin importar relativa intente:
fuente
Creo que la mejor solución es crear un paquete para su módulo: aquí hay más información sobre cómo hacerlo.
Una vez que tenga un paquete, no necesita preocuparse por la importación relativa, solo puede hacer importaciones absolutas.
fuente
Tuve un problema similar: necesitaba un servicio de Linux y un complemento cgi que usaran constantes comunes para cooperar. La forma 'natural' de hacer esto es colocarlos en el init archivo .py del paquete, pero no puedo iniciar el complemento cgi con el parámetro -m.
Mi solución final fue similar a la Solución # 2 anterior:
La desventaja es que debe anteponer las constantes (o funciones comunes) con pkg:
fuente