Importações relativas - ModuleNotFoundError: nenhum módulo chamado x

179

Esta é a primeira vez que realmente me sentei e tentei o python 3, e parece estar falhando miseravelmente. Eu tenho os dois arquivos a seguir:

  1. test.py
  2. config.py

O config.py possui algumas funções definidas, além de algumas variáveis. Eu reduzi para o seguinte:

config.py

debug = True

test.py

import config
print (config.debug)

Eu também tenho um __init__.py

No entanto, estou recebendo o seguinte erro:

ModuleNotFoundError: No module named 'config'

Estou ciente de que a convenção py3 é usar importações absolutas:

from . import config

No entanto, isso leva ao seguinte erro:

ImportError: cannot import name 'config'

Então, eu estou sem saber o que fazer aqui ... Qualquer ajuda é muito apreciada. :)

Blitzmann
fonte
Não consigo reproduzir o erro, como você executa esse código?
Copperfield 05/05
2
Eu o executo com o ocioso que acompanha o python, e também como python test.py, e funciona perfeitamente bem. Eu não tenho PyCharm, mas talvez alguma má configuração do PyCharm que está causando o problema
Copperfield
1
Muito estranho. Estou usando o WinPython - basta baixar o vanilla Python 3.6 do python.org e ele funciona bem. Nunca pensei em verificar o intérprete! Obrigado!
blitzmann
1
Meu palpite é que algo estranho está acontecendo com PYTHONPATH. Verifique suas configurações de IDE e / ou variáveis ​​de ambiente do sistema.
Martin Tournoij
1
Eu tenho esse mesmo problema exato. Não é pycharm! É python3. Funciona em python2, mas ao usar python3, você vê este erro! muito frustrante.

Respostas:

164

TL; DR: você não pode fazer importações relativas do arquivo que você executa, pois o __main__módulo não faz parte de um pacote.

Importações absolutas - importe algo disponível nosys.path

Importações relativas - importe algo relativo ao módulo atual, faça parte de um pacote

Se você estiver executando as duas variantes exatamente da mesma maneira, uma delas funcionará. De qualquer forma, aqui está um exemplo que deve ajudá-lo a entender o que está acontecendo, vamos adicionar outro main.pyarquivo com a estrutura geral de diretórios como esta:

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

E vamos atualizar o test.py para ver o que está acontecendo:

# 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

Vamos executar o test.py primeiro:

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

Aqui "test" é o __main__módulo e não sabe nada sobre pertencer a um pacote. No entanto, import configdeve funcionar, pois a ryanpasta será adicionada ao sys.path.

Vamos executar o main.py:

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

E aqui o teste está dentro do pacote "ryan" e pode executar importações relativas. import configfalha, pois importações relativas implícitas não são permitidas no Python 3.

Espero que isso tenha ajudado.

PS: se você estiver usando o Python 3, não haverá mais necessidade de __init__.pyarquivos.

Igonato
fonte
3
Existe algo que eu possa fazer para que as importações absolutas sempre funcionem? Tipo, ligar para sys.path.append('/some/path/my_module')dentro /some/path/my_module/__init__.py?
James T.
4
@JamesT. Sim, é bastante comum modificar sys.pathdurante o tempo de execução ( github.com/… ). Você também pode definir a variável de ambiente PYTHONPATH.
Igonato
6
"se você estiver usando o Python 3, não haverá mais necessidade de __init__.pyarquivos." Interessante. Você pode elaborar sobre isso? Fiquei com a impressão de que o mecanismo de resolução de pacote não mudou tanto assim entre 2 e 3.
Kevin
2
"se você está usando o Python 3, não há mais necessidade de __init__.pyarquivos." Por outro lado, você pode descrever as coisas se queremos que um pacote funcione em 2 e 3? E veja o lamentavelmente desatualizado 2009 Para que serve __init__.py? e sua resposta mais votada "É parte de um pacote" . Precisamos começar a enfatizar a distinção "pacote regular [antigo, anterior a 3.3]] vs " pacote de namespace [3.3+] "em todos os lugares e com frequência.
smci 8/05/19
61

Eu descobri. Muito frustrante, especialmente vindo do python2.

Você precisa adicionar um .ao módulo, independentemente de ser relativo ou absoluto.

Eu criei a configuração do diretório da seguinte maneira.

/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()

quando executo main, é isso que acontece

$ 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'

Corri 2to3, e a saída principal era essa

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

Eu tive que modificar a declaração de importação do mody.py para corrigi-la

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


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

Então eu corri o main.py novamente e obtive a saída esperada

$ python main.py
I gave you this string.

Por fim, apenas para limpá-lo e torná-lo portátil entre 2 e 3.

from __future__ import absolute_import
from .modx import does_something

fonte
1
Vale ressaltar que o try/exceptprocedimento de carregamento é o ingrediente real que funciona aqui (como algumas pessoas precisarão usar try:scripts.modxe except: modx) e foi o que resolveu esse problema para mim.
Justapigeon 01/03
40

A configuração de PYTHONPATH também pode ajudar com esse problema.

Aqui está como isso pode ser feito no Windows

set PYTHONPATH=.

Santosh Pillai
fonte
11
definir PYTHONPATH para o diretório principal de códigos resolveu o problema para mim!
22718 Geek
1
Funciona também no Linux. exportação PYTHONPATH =.
rjdkolb 3/03
29

Você precisa anexar o caminho do módulo PYTHONPATH.


Para UNIX (Linux, OSX, ...)

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

Para Windows

set PYTHONPATH=%PYTHONPATH%;C:\path\to\your\module\
Giorgos Myrianthous
fonte
4
Muito obrigado @Giorgos! Isso é especialmente verdade quando você está tentando definir um diretório raiz em uma imagem do Docker.
Tony Fraser
15

Tentei seu exemplo

from . import config

obteve o seguinte SystemError:
/usr/bin/python3.4 test.py
Traceback (última chamada mais recente):
arquivo "test.py", linha 1,
de. import config
SystemError: módulo pai '' não carregado, não pode executar importação relativa


Isso funcionará para mim:

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

>>>debug=True

Testado com Python: 3.4.2 - PyCharm 2016.3.2


Ao lado deste PyCharm, você pode importar este nome .
Você precisa clicar confige um ícone de ajuda aparece. insira a descrição da imagem aqui

stovfl
fonte
11

Você pode simplesmente adicionar o seguinte arquivo ao diretório de testes e, em seguida, o python o executará antes dos testes

__init__.py file

import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
Vinod Rane
fonte
Era exatamente isso que eu estava procurando. Obrigado por compartilhar esta resposta !!!
Mayur
Fazer algo assim gera um erro de linter (pylint3). O erro é semelhante a este. filename.py:12abled: C0413: Import "import abc.def.ghi.file_util as file_util" deve ser colocado na parte superior do módulo (posição de importação incorreta)
Sharad
6

Defina PYTHONPATHa variável de ambiente no diretório raiz do projeto.

Considerando o UNIX:

export PYTHONPATH=.
manasouza
fonte
4

Este exemplo funciona no Python 3.6.

Eu sugiro ir Run -> Edit Configurationsno PyCharm, excluir todas as entradas e tentar executar o código através do PyCharm novamente.

Se isso não funcionar, verifique o intérprete do projeto (Configurações -> Intérprete do projeto) e execute os padrões de configuração (Executar -> Editar configurações ...).

Carson Crane
fonte
4

Declare a lista sys.path correta antes de chamar o 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
fonte
1

Como foi declarado nos comentários da postagem original, este parecia ser um problema com o intérprete python que eu estava usando por qualquer motivo, e não algo de errado com os scripts python. Eu mudei do pacote WinPython para o python oficial 3.6 do python.org e funcionou muito bem. Obrigado pela ajuda de todos :)

Blitzmann
fonte
1
Hmm odeio dizer isso, mas a mesma coisa aconteceu comigo. O ambiente de recriação corrige o problema. No meu caso, eu estava recebendo esse erro ao executar testes. No mesmo ambiente, tente importar o mesmo módulo que funcionou. Recriando o ambiente fixo todos eles (mesmo python versão 3.6)
naoko
1
IDEs diferentes têm uma maneira diferente de lidar com caminhos, especialmente para arquivos de origem do projeto (visualizações, módulos, modelos etc.) Se o seu projeto estiver estruturado e codificado corretamente, ele deverá funcionar com todos os IDEs (padrão). Ter problemas com IDE populares como o WinPython significa que o problema realmente vem do seu projeto. Como mencionado acima, o problema é "Você precisa adicionar um. Ao módulo" pelo user3159377, que deve ser a resposta aceita.
winux 12/12/18
1

Se você estiver usando python 3+, tente adicionar as linhas abaixo

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
fonte
0

Experimentar

from . import config

O que isso faz é importar do mesmo nível de pasta. Se você tentar importá-lo diretamente, assume que é um subordinado

Arny Boy
fonte