Importaciones relativas - ModuleNotFoundError: ningún módulo llamado x

179

Esta es la primera vez que realmente me senté y probé Python 3, y parece estar fallando miserablemente. Tengo los siguientes dos archivos:

  1. prueba.py
  2. config.py

config.py tiene algunas funciones definidas, así como algunas variables. Lo he despojado a lo siguiente:

config.py

debug = True

prueba.py

import config
print (config.debug)

Yo tambien tengo un __init__.py

Sin embargo, recibo el siguiente error:

ModuleNotFoundError: No module named 'config'

Soy consciente de que la convención py3 es usar importaciones absolutas:

from . import config

Sin embargo, esto lleva al siguiente error:

ImportError: cannot import name 'config'

Así que no sé qué hacer aquí ... Cualquier ayuda es muy apreciada. :)

blitzmann
fuente
No puedo reproducir el error, ¿cómo ejecutas este código?
Copperfield
2
Lo ejecuto con inactivo que viene con python, y también como python test.py, y funciona perfectamente bien. No tengo pyCharm, pero quizás haya alguna configuración incorrecta de pyCharm que esté causando el problema
Copperfield
1
Muy raro. Estoy usando WinPython: solo descargue Vanilla Python 3.6 de python.org, y funciona bien. ¡Nunca pensé en consultar al intérprete! ¡Gracias!
blitzmann
1
Supongo que está sucediendo algo funky con PYTHONPATH. Verifique su configuración IDE y / o las variables de entorno del sistema.
Martin Tournoij
1
Tengo este mismo problema exacto. ¡No es pycharm! Es python3. Funciona en python2, pero cuando usas python3, ¡ves este error! Muy frustrante.

Respuestas:

164

TL; DR: no puede realizar importaciones relativas desde el archivo que ejecuta, ya que el __main__módulo no forma parte de un paquete.

Importaciones absolutas : importe algo disponible ensys.path

Importaciones relativas : importar algo relacionado con el módulo actual, debe ser parte de un paquete

Si ejecuta ambas variantes exactamente de la misma manera, una de ellas debería funcionar. De todos modos, aquí hay un ejemplo que debería ayudarlo a comprender lo que está sucediendo, agreguemos otro main.pyarchivo con la estructura general de directorios de esta manera:

.
./main.py
./ryan/__init__.py
./ryan/config.py
./ryan/test.py

Y vamos a actualizar test.py para ver qué está pasando:

# config.py
debug = True


# test.py
print(__name__)

try:
    # Trying to find module in the parent package
    from . import config
    print(config.debug)
    del config
except ImportError:
    print('Relative import failed')

try:
    # Trying to find module on sys.path
    import config
    print(config.debug)
except ModuleNotFoundError:
    print('Absolute import failed')
# main.py
import ryan.test

Ejecutemos test.py primero:

$ python ryan/test.py
__main__
Relative import failed
True

Aquí "prueba" es el __main__módulo y no sabe nada acerca de pertenecer a un paquete. Sin embargo, import configdebería funcionar, ya que la ryancarpeta se agregará a sys.path.

Ejecutemos main.py en su lugar:

$ python main.py
ryan.test
True
Absolute import failed

Y aquí la prueba está dentro del paquete "ryan" y puede realizar importaciones relativas. import configfalla ya que las importaciones relativas implícitas no están permitidas en Python 3.

Espero que esto haya ayudado.

PD: si te quedas con Python 3 no hay más necesidad de __init__.pyarchivos.

Igonato
fuente
3
¿Hay algo que pueda hacer para que las importaciones absolutas siempre funcionen? Como, llamar sys.path.append('/some/path/my_module')dentro de /some/path/my_module/__init__.py?
James T.
44
@JamesT. Sí, es bastante común modificar sys.pathdurante el tiempo de ejecución ( github.com/… ). También puede establecer la variable de entorno PYTHONPATH.
Igonato
66
"si te quedas con Python 3 no hay más necesidad de __init__.pyarchivos". Interesante. ¿Puedes dar más detalles sobre esto? Yo tenía la impresión de que el mecanismo de resolución de paquete no ha cambiado mucho entre 2 y 3.
Kevin
2
"si te quedas con Python 3 no hay más necesidad de __init__.pyarchivos". Por el contrario, ¿puede describir cosas si queremos que un paquete funcione tanto en 2 como en 3? Y vea el lamentablemente desactualizado 2009 ¿ __init__.pyPara qué sirve ? y su respuesta más votada "Es parte de un paquete" . Necesitamos comenzar a enfatizar la distinción "paquete regular [antiguo, anterior a 3.3]" vs "paquete de espacio de nombres [3.3+]" en todas partes y con frecuencia.
smci
61

Me lo imaginé. Muy frustrante, especialmente viniendo de python2.

Debe agregar .a al módulo, independientemente de si es relativo o absoluto.

Creé la configuración del directorio de la siguiente manera.

/main.py
--/lib
  --/__init__.py
  --/mody.py
  --/modx.py

modx.py

def does_something():
    return "I gave you this string."

mody.py

from modx import does_something

def loaded():
    string = does_something()
    print(string)

main.py

from lib import mody

mody.loaded()

cuando ejecuto main, esto es lo que sucede

$ python main.py
Traceback (most recent call last):
  File "main.py", line 2, in <module>
    from lib import mody
  File "/mnt/c/Users/Austin/Dropbox/Source/Python/virtualenviron/mock/package/lib/mody.py", line 1, in <module>
    from modx import does_something
ImportError: No module named 'modx'

Ejecuté 2to3, y el resultado central fue este

RefactoringTool: Refactored lib/mody.py
--- lib/mody.py (original)
+++ lib/mody.py (refactored)
@@ -1,4 +1,4 @@
-from modx import does_something
+from .modx import does_something

 def loaded():
     string = does_something()
RefactoringTool: Files that need to be modified:
RefactoringTool: lib/modx.py
RefactoringTool: lib/mody.py

Tuve que modificar la declaración de importación de mody.py para solucionarlo

try:
    from modx import does_something
except ImportError:
    from .modx import does_something


def loaded():
    string = does_something()
    print(string)

Luego ejecuté main.py nuevamente y obtuve el resultado esperado

$ python main.py
I gave you this string.

Por último, solo para limpiarlo y hacerlo portátil entre 2 y 3.

from __future__ import absolute_import
from .modx import does_something

fuente
1
Vale la pena señalar que el try/exceptprocedimiento de carga es el ingrediente real que funciona aquí (como algunas personas necesitarán usar try:scripts.modxy except: modx), y fue lo que resolvió este problema para mí.
Justapigeon
40

Establecer PYTHONPATH también puede ayudar con este problema.

Así es como se puede hacer en Windows

set PYTHONPATH=.

Santosh Pillai
fuente
11
¡configurar PYTHONPATH en el directorio del código principal me resolvió el problema!
Geek
1
Funciona en Linux también. export PYTHONPATH =.
rjdkolb
29

Debe agregar la ruta del módulo a PYTHONPATH.


Para UNIX (Linux, OSX, ...)

export PYTHONPATH="${PYTHONPATH}:/path/to/your/module/"

Para ventanas

set PYTHONPATH=%PYTHONPATH%;C:\path\to\your\module\
Giorgos Myrianthous
fuente
44
Muchas gracias @Giorgos! Esto es especialmente cierto cuando intenta establecer un directorio raíz en una imagen acoplable.
Tony Fraser
15

Probé tu ejemplo

from . import config

obtuvo el siguiente SystemError:
/usr/bin/python3.4 test.py
Traceback (última llamada más reciente):
Archivo "test.py", línea 1,
desde. import config
SystemError: el módulo principal '' no está cargado, no puede realizar una importación relativa


Esto funcionará para mí:

import config
print('debug=%s'%config.debug)

>>>debug=True

Probado con Python: 3.4.2 - PyCharm 2016.3.2


Además de esto, PyCharm te ofrece Importar este nombre .
Tiene que hacer clic configy aparece un icono de ayuda . ingrese la descripción de la imagen aquí

Stovfl
fuente
11

Simplemente puede agregar el siguiente archivo a su directorio de pruebas, y luego Python lo ejecutará antes de las pruebas

__init__.py file

import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
Vinod Rane
fuente
Esto es lo que estaba buscando exactamente. ¡Gracias por compartir esta respuesta!
Mayur
Hacer algo como esto da un error de linter (pylint3). El error es similar a este. filename.py:12:0: C0413: la importación "import abc.def.ghi.file_util as file_util" debe colocarse en la parte superior del módulo (posición de importación incorrecta)
Sharad
6

Establezca PYTHONPATHla variable de entorno en el directorio del proyecto raíz.

Considerando como UNIX:

export PYTHONPATH=.
Manasouza
fuente
4

Este ejemplo funciona en Python 3.6.

Sugiero ir a Run -> Edit ConfigurationsPyCharm, eliminar cualquier entrada allí e intentar ejecutar el código a través de PyCharm nuevamente.

Si eso no funciona, verifique su intérprete de proyecto (Configuración -> Intérprete de proyecto) y ejecute los valores predeterminados de configuración (Ejecutar -> Editar configuraciones ...).

Grúa Carson
fuente
4

Declare la lista correcta de sys.path antes de llamar al módulo:

import os, sys

#'/home/user/example/parent/child'
current_path = os.path.abspath('.')

#'/home/user/example/parent'
parent_path = os.path.dirname(current_path)

sys.path.append(parent_path)
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'child.settings')
HoangYell
fuente
1

Como se indicó en los comentarios a la publicación original, esto parecía ser un problema con el intérprete de Python que estaba usando por cualquier razón, y no algo mal con los scripts de Python. Cambié del paquete WinPython al python 3.6 oficial de python.org y funcionó bien. gracias por la ayuda a todos :)

blitzmann
fuente
1
Hmm odio decir esto, pero me pasó lo mismo. El entorno de recreación soluciona el problema. En mi caso, recibí este error al ejecutar pruebas. En el mismo entorno, intente importar el mismo módulo trabajado. El entorno de recreación los arregló a todos (la misma versión de Python 3.6)
naoko
1
Diferentes IDE tienen diferentes formas de manejar las rutas especialmente para los archivos fuente del proyecto (vistas, módulos, plantillas, etc.) Si su proyecto está estructurado y codificado correctamente, entonces debería funcionar para todos los IDE (estándar). Tener problemas con IDE populares como WinPython significa que el problema realmente proviene de su proyecto. Como se mencionó anteriormente, el problema es "Debe agregar un. Al módulo" por user3159377, que debería ser la respuesta aceptada.
winux
1

Si está utilizando Python 3+, intente agregar las siguientes líneas

import os, sys
dir_path = os.path.dirname(os.path.realpath(__file__))
parent_dir_path = os.path.abspath(os.path.join(dir_path, os.pardir))
sys.path.insert(0, parent_dir_path)
Vivek Garg
fuente
0

Tratar

from . import config

Lo que hace es importar desde el mismo nivel de carpeta. Si intentas importarlo directamente, se supone que es un subordinado

Arny Boy
fuente