Impor relatif - ModuleNotFoundError: Tidak ada modul bernama x

179

Ini adalah pertama kalinya saya benar-benar duduk dan mencoba python 3, dan tampaknya gagal total. Saya memiliki dua file berikut:

  1. test.py
  2. config.py

config.py memiliki beberapa fungsi yang didefinisikan di dalamnya serta beberapa variabel. Saya telah menanggalkannya sebagai berikut:

config.py

debug = True

test.py

import config
print (config.debug)

Saya juga punya __init__.py

Namun, saya mendapatkan kesalahan berikut:

ModuleNotFoundError: No module named 'config'

Saya sadar bahwa konvensi py3 adalah menggunakan impor absolut:

from . import config

Namun, ini mengarah pada kesalahan berikut:

ImportError: cannot import name 'config'

Jadi saya bingung apa yang harus dilakukan di sini ... Setiap bantuan sangat dihargai. :)

blitzmann
sumber
Saya tidak dapat mereproduksi kesalahan, bagaimana Anda menjalankan kode ini?
Copperfield
2
Saya menjalankannya dengan idle yang datang dengan python, dan juga python test.py, dan itu berfungsi dengan baik. Saya tidak punya pyCharm, tapi mungkin ada beberapa konfigurasi pyCharm yang menyebabkan masalah
Copperfield
1
Sangat aneh. Saya menggunakan WinPython - cukup unduh vanilla Python 3.6 dari python.org, dan berfungsi dengan baik. Tidak pernah terpikir untuk memeriksa juru bahasa! Terima kasih!
blitzmann
1
Dugaan saya adalah bahwa sesuatu yang funky sedang terjadi dengan PYTHONPATH. Periksa pengaturan IDE Anda dan / atau variabel lingkungan sistem.
Martin Tournoij
1
Saya memiliki masalah yang sama persis ini. Ini bukan pycharm! Itu adalah python3. Ia bekerja di python2, tetapi ketika menggunakan python3, Anda melihat kesalahan ini! sangat membuat frustrasi.

Jawaban:

164

TL; DR: Anda tidak dapat melakukan impor relatif dari file yang Anda jalankan sejak itu__main__ modul bukan bagian dari paket.

Impor absolut - impor sesuatu yang tersedia disys.path

Impor relatif - mengimpor sesuatu yang relatif ke modul saat ini, harus menjadi bagian dari paket

Jika Anda menjalankan kedua varian dengan cara yang persis sama, salah satunya harus bekerja. Bagaimanapun, berikut adalah contoh yang akan membantu Anda memahami apa yang terjadi, mari tambahkan main.pyfile lain dengan struktur direktori keseluruhan seperti ini:

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

Dan mari kita perbarui test.py untuk melihat apa yang terjadi:

# 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

Mari kita jalankan test.py terlebih dahulu:

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

Berikut "test" adalah yang __main__modul dan tidak tahu apa-apa tentang milik sebuah paket. Namun import configharus bekerja, karenaryan folder akan ditambahkan ke sys.path.

Mari kita jalankan main.py sebagai gantinya:

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

Dan di sini tes ada di dalam paket "ryan" dan dapat melakukan impor relatif. import configgagal karena impor relatif implisit tidak diizinkan dalam Python 3.

Semoga ini bisa membantu.

PS: jika Anda tetap menggunakan Python 3 maka tidak perlu lagi ada __init__.pyfile.

Igonato
sumber
3
Apakah ada yang bisa saya lakukan agar impor absolut selalu berfungsi? Seperti, menelepon sys.path.append('/some/path/my_module')bagian dalam /some/path/my_module/__init__.py?
James T.
4
@ JamesT. Ya, sangat umum untuk memodifikasi sys.pathselama runtime ( github.com/... ). Anda juga dapat mengatur variabel lingkungan PYTHONPATH.
Igonato
6
"Jika Anda tetap menggunakan Python 3 maka tidak perlu lagi ada __init__.pyfile." Menarik. Bisakah Anda menguraikan ini? Saya mendapat kesan bahwa mekanisme resolusi paket tidak banyak berubah antara 2 dan 3.
Kevin
2
"Jika Anda tetap menggunakan Python 3, tidak ada lagi kebutuhan untuk __init__.pyfile." Sebaliknya, dapatkah Anda mendeskripsikan hal-hal jika kami ingin sebuah paket berfungsi dalam 2 dan 3? Dan lihat 2009 yang sudah ketinggalan zaman. Untuk apa __init__.py? dan jawabannya yang paling tinggi "Ini adalah bagian dari paket" . Kita perlu mulai menekankan perbedaan "paket reguler [lama, pra-3.3]" vs "paket namespace [3.3+]" di mana-mana dan sering.
smci
61

Saya menemukan jawabannya. Sangat frustasi, terutama yang berasal dari python2.

Anda harus menambahkan a .ke modul, terlepas dari apakah itu relatif atau absolut.

Saya membuat pengaturan direktori sebagai berikut.

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

ketika saya mengeksekusi main, inilah yang terjadi

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

Saya berlari 2to3, dan output inti adalah ini

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

Saya harus mengubah pernyataan impor mody.py untuk memperbaikinya

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


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

Kemudian saya menjalankan main.py lagi dan mendapatkan output yang diharapkan

$ python main.py
I gave you this string.

Terakhir, hanya untuk membersihkannya dan membuatnya portabel antara 2 dan 3.

from __future__ import absolute_import
from .modx import does_something

sumber
1
Perlu dicatat bahwa try/exceptprosedur pemuatan adalah bahan asli yang bekerja di sini (karena beberapa orang perlu menggunakan try:scripts.modxdan except: modx), dan itulah yang memecahkan masalah ini untuk saya.
Justapigeon
40

Pengaturan PYTHONPATH juga dapat membantu masalah ini.

Berikut ini cara melakukannya pada Windows

set PYTHONPATH=.

Santosh Pillai
sumber
11
pengaturan PYTHONPATH ke direktori kode utama memecahkan masalah bagi saya!
Geek
1
Bekerja di Linux juga. ekspor PYTHONPATH =.
rjdkolb
29

Anda harus menambahkan jalur modul ke PYTHONPATH.


Untuk UNIX (Linux, OSX, ...)

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

Untuk Windows

set PYTHONPATH=%PYTHONPATH%;C:\path\to\your\module\
Giorgos Myrianthous
sumber
4
Terima kasih banyak @Giorgos! Ini terutama benar ketika Anda mencoba untuk mengatur direktori root di gambar buruh pelabuhan.
Tony Fraser
15

Coba contoh Anda

from . import config

mendapat SystemError berikut:
/usr/bin/python3.4 test.py
Traceback (panggilan terakhir terakhir):
File "test.py", baris 1, masuk
dari. import config
SystemError: Modul induk '' tidak dimuat, tidak dapat melakukan impor relatif


Ini akan bekerja untuk saya:

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

>>>debug=True

Diuji dengan Python: 3.4.2 - PyCharm 2016.3.2


Di samping PyCharm ini menawarkan Anda untuk Mengimpor nama ini .
Anda harus mengklik configdan ikon bantuan muncul. masukkan deskripsi gambar di sini

stovfl
sumber
11

Anda cukup menambahkan file berikut ke direktori tes Anda, dan kemudian python akan menjalankannya sebelum tes

__init__.py file

import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
Vinod Rane
sumber
Inilah yang sebenarnya saya cari. Terima kasih telah membagikan jawaban ini !!!
Mayur
Melakukan sesuatu seperti ini memberikan kesalahan linter (pylint3). Kesalahannya mirip dengan yang ini. filename.py:12-02: C0413: Impor "import abc.def.ghi.file_util sebagai file_util" harus ditempatkan di bagian atas modul (posisi salah-impor)
Sharad
6

Set PYTHONPATH variabel lingkungan di direktori proyek root.

Mempertimbangkan seperti-UNIX:

export PYTHONPATH=.
manasouza
sumber
4

Contoh ini berfungsi pada Python 3.6.

Saya sarankan pergi ke Run -> Edit Configurations PyCharm, menghapus semua entri di sana, dan mencoba menjalankan kode melalui PyCharm lagi.

Jika itu tidak berhasil, periksa penerjemah proyek Anda (Pengaturan -> Project Interpreter) dan jalankan konfigurasi default (Jalankan -> Edit Konfigurasi ...).

Carson Crane
sumber
4

Nyatakan daftar sys.path yang benar sebelum Anda memanggil modul:

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
sumber
1

Seperti yang dinyatakan dalam komentar ke posting asli, ini tampaknya menjadi masalah dengan interpreter python yang saya gunakan untuk alasan apa pun, dan bukan sesuatu yang salah dengan skrip python. Saya beralih dari bundel WinPython ke python resmi 3.6 dari python.org dan bekerja dengan baik. terima kasih atas bantuan semuanya :)

blitzmann
sumber
1
Hmm benci mengatakan ini tetapi hal yang sama terjadi pada saya. Lingkungan rekreasi memperbaiki masalah. Dalam kasus saya, saya mendapatkan kesalahan ini saat menjalankan tes. Di lingkungan yang sama, upaya untuk mengimpor modul yang sama berhasil. Lingkungan rekreasi memperbaiki semuanya (python versi yang sama 3.6)
naoko
1
IDE yang berbeda memiliki cara penanganan jalur yang khusus untuk file sumber proyek (tampilan, modul, template, dll.) Jika proyek Anda terstruktur dan dikodekan dengan benar, maka itu harus bekerja untuk semua IDE (standar). Memiliki masalah dengan IDE populer seperti WinPython berarti masalahnya memang berasal dari proyek Anda. Seperti disebutkan di atas, masalahnya adalah "Anda harus menambahkan. Ke modul" oleh user3159377 yang seharusnya menjadi jawaban yang diterima.
winux
1

Jika Anda menggunakan python 3+ maka coba tambahkan baris di bawah ini

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

Mencoba

from . import config

Apa yang dilakukan adalah mengimpor dari level folder yang sama. Jika Anda langsung mencoba mengimpornya anggap itu bawahan

Arny Boy
sumber