Względne importy - ModuleNotFoundError: Brak modułu o nazwie x

178

To pierwszy raz, kiedy naprawdę usiadłem i wypróbowałem Python 3 i wydaje mi się, że zawodzi. Mam następujące dwa pliki:

  1. test.py
  2. config.py

config.py ma zdefiniowane w nim kilka funkcji, a także kilka zmiennych. Ograniczyłem to do następujących:

config.py

debug = True

test.py

import config
print (config.debug)

Mam też __init__.py

Jednak pojawia się następujący błąd:

ModuleNotFoundError: No module named 'config'

Zdaję sobie sprawę, że konwencja py3 zakłada import bezwzględny:

from . import config

Prowadzi to jednak do następującego błędu:

ImportError: cannot import name 'config'

Więc nie wiem, co robić tutaj ... Każda pomoc jest bardzo mile widziana. :)

blitzmann
źródło
Nie mogę odtworzyć błędu, jak wykonujesz ten kod?
Copperfield
2
Wykonuję go z bezczynnością, która pochodzi z Pythona, a także jako python test.py, i działa doskonale. Nie mam pyCharm, ale być może jest to zła konfiguracja pyCharm, która powoduje problem
Copperfield
1
Bardzo dziwne. Używam WinPython - po prostu pobierz Vanilla Python 3.6 z python.org i działa dobrze. Nigdy nie pomyślałem, żeby sprawdzić tłumacza! Dzięki!
blitzmann
1
Domyślam się, że z PYTHONPATH dzieje się coś dziwnego. Sprawdź ustawienia IDE i / lub zmienne środowiskowe systemu.
Martin Tournoij
1
Mam dokładnie ten sam problem. To nie jest pycharm! To jest python3. Działa w python2, ale kiedy używasz python3, widzisz ten błąd! bardzo frustrujące.

Odpowiedzi:

163

TL; DR: nie można zrobić import względne z pliku, który wykonania ponieważ __main__moduł nie jest częścią pakietu.

Import bezwzględny - zaimportuj coś, co jest dostępne nasys.path

Importy względne - zaimportuj coś odnoszącego się do bieżącego modułu, musi być częścią pakietu

Jeśli uruchamiasz oba warianty dokładnie w ten sam sposób, jeden z nich powinien działać. W każdym razie, oto przykład, który powinien pomóc ci zrozumieć, co się dzieje, dodajmy kolejny main.pyplik z ogólną strukturą katalogów, taką jak ta:

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

Zaktualizujmy test.py, aby zobaczyć, co się dzieje:

# 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

Najpierw uruchommy test.py:

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

Tutaj „test” to plik__main__ moduł i wie nic o przynależności do pakietu nie wiem. import configPowinno jednak działać, ponieważ ryanfolder zostanie dodany do sys.path.

Zamiast tego uruchommy main.py:

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

A tutaj test znajduje się w pakiecie "ryan" i może wykonywać import względny. import configkończy się niepowodzeniem, ponieważ niejawne importy względne nie są dozwolone w Pythonie 3.

Mam nadzieję, że to pomogło.

PS: jeśli trzymasz się Pythona 3, __init__.pypliki nie są już potrzebne .

Igonato
źródło
3
Czy jest coś, co mogę zrobić, aby import absolutny zawsze działał? Na przykład zadzwoń do sys.path.append('/some/path/my_module')środka /some/path/my_module/__init__.py?
James T.
4
@JamesT. Tak, modyfikowanie w sys.pathczasie wykonywania jest dość powszechne ( github.com/ ... ). Możesz także ustawić zmienną środowiskową PYTHONPATH.
Igonato,
6
„jeśli trzymasz się Pythona 3, __init__.pypliki nie są już potrzebne ”. Ciekawy. Czy możesz to rozwinąć? Miałem wrażenie, że mechanizm rozwiązywania pakietów nie zmienił się aż tak bardzo między 2 a 3
Kevin
2
„jeśli trzymasz się języka Python 3, __init__.pypliki nie są już potrzebne ”. I odwrotnie, czy możesz opisać rzeczy, jeśli chcemy, aby pakiet działał zarówno w 2, jak i 3? I zobacz żałośnie nieaktualny rok 2009 Do czego służy __init__.py? i najbardziej popularna odpowiedź: „Jest to część pakietu” . Musimy zacząć podkreślać rozróżnienie „zwykły pakiet [stary, przed 3.3]” a „pakiet przestrzeni nazw [3.3+]” wszędzie i często.
smci
61

Rozgryzłem to. Bardzo frustrujące, zwłaszcza pochodzące z pythona2.

Musisz dodać . do modułu, niezależnie od tego, czy jest względne, czy bezwzględne.

Utworzyłem konfigurację katalogu w następujący sposób.

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

kiedy wykonuję main, tak się dzieje

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

Uruchomiłem 2to3, a podstawowe wyjście było takie

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

Musiałem zmodyfikować instrukcję importu mody.py, aby to naprawić

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


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

Następnie ponownie uruchomiłem main.py i uzyskałem oczekiwany wynik

$ python main.py
I gave you this string.

Na koniec wystarczy go wyczyścić i uczynić przenośnym między 2 a 3.

from __future__ import absolute_import
from .modx import does_something

źródło
1
Warto zauważyć, że try/exceptprocedura ładowania jest prawdziwym składnikiem, który tutaj działa (ponieważ niektórzy będą musieli używać try:scripts.modxi except: modx), i to właśnie rozwiązało ten problem.
Justapigeon
40

Ustawienie PYTHONPATH również może pomóc w rozwiązaniu tego problemu.

Oto, jak można to zrobić w systemie Windows

set PYTHONPATH=.

Santosh Pillai
źródło
11
ustawienie PYTHONPATH na główny katalog kodu rozwiązało problem za mnie!
Geek
1
Działa również w systemie Linux. eksportuj PYTHONPATH =.
rjdkolb
29

Musisz dołączyć ścieżkę do modułu do PYTHONPATH.


W przypadku systemu UNIX (Linux, OSX, ...)

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

Dla Windowsa

set PYTHONPATH=%PYTHONPATH%;C:\path\to\your\module\
Giorgos Myrianthous
źródło
4
Wielkie dzięki @Giorgos! Jest to szczególnie ważne, gdy próbujesz ustawić katalog główny w obrazie Dockera.
Tony Fraser
15

Próbowałem twojego przykładu

from . import config

otrzymałem następujący błąd SystemError:
/usr/bin/python3.4 test.py
Traceback (ostatnie wywołanie ostatnie):
Plik "test.py", wiersz 1,
z. import config
SystemError: Moduł nadrzędny „” nie został załadowany, nie można wykonać importu względnego


To zadziała dla mnie:

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

>>>debug=True

Przetestowano w Pythonie: 3.4.2 - PyCharm 2016.3.2


Oprócz tego PyCharm oferuje import tej nazwy .
Musisz kliknąć configi pojawi się ikona pomocy . wprowadź opis obrazu tutaj

stovfl
źródło
11

Możesz po prostu dodać następujący plik do katalogu testing, a następnie Python uruchomi go przed testami

__init__.py file

import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
Vinod Rane
źródło
Właśnie tego szukałem. Dzięki za udostępnienie tej odpowiedzi !!!
Mayur
Wykonanie czegoś takiego powoduje błąd lintera (pylint3). Błąd jest podobny do tego. nazwa_pliku.py:12:0: C0413: Import "import abc.def.ghi.file_util as file_util" powinien być umieszczony na górze modułu (niewłaściwa pozycja importu)
Sharad
6

Ustaw PYTHONPATHzmienną środowiskową w głównym katalogu projektu.

Biorąc pod uwagę system UNIX:

export PYTHONPATH=.
manasouza
źródło
4

Ten przykład działa na Pythonie 3.6.

Proponuję pójść Run -> Edit Configurations PyCharm, usunąć tam wszystkie wpisy i spróbować ponownie uruchomić kod przez PyCharm.

Jeśli to nie zadziała, sprawdź interpreter projektu (Ustawienia -> Interpreter projektu) i uruchom ustawienia domyślne (Uruchom -> Edytuj konfiguracje ...).

Carson Crane
źródło
4

Zadeklaruj poprawną listę sys.path przed wywołaniem modułu:

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
źródło
1

Jak stwierdzono w komentarzach do oryginalnego postu, wydawało się, że jest to problem z interpretatorem języka Python, którego używałem z dowolnego powodu, a nie coś złego ze skryptami Pythona. Przerzuciłem się z pakietu WinPython na oficjalny Python 3.6 z python.org i wszystko działało dobrze. dzięki za pomoc wszystkim :)

blitzmann
źródło
1
Hmm, przykro mi to mówić, ale mi się przydarzyło to samo. Odtwarzanie środowiska rozwiązuje problem. W moim przypadku otrzymywałem ten błąd podczas uruchamiania testów. W tym samym środowisku próba zaimportowania tego samego modułu zadziałała. Środowisko
odtwarzania
1
Różne środowiska IDE mają różne sposoby obsługi ścieżek, szczególnie dla plików źródłowych projektu (widoki, moduły, szablony itp.). Jeśli projekt jest odpowiednio skonstruowany i zakodowany, powinien działać we wszystkich (standardowych) IDE. Problemy z popularnymi IDE, takimi jak WinPython, oznaczają, że problem rzeczywiście pochodzi z twojego projektu. Jak wspomniano powyżej, problemem jest „Musisz dodać a. Do modułu” użytkownika3159377, co powinno być zaakceptowaną odpowiedzią.
winux
1

Jeśli używasz Pythona 3+, spróbuj dodać poniższe wiersze

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
źródło
0

Próbować

from . import config

To, co to robi, to importowanie z tego samego poziomu folderu. Jeśli spróbujesz bezpośrednio zaimportować, zakłada się, że jest to podwładny

Arny Boy
źródło