Saya ingin mengimpor fungsi dari file lain di direktori yang sama.
Kadang-kadang bekerja dengan saya from .mymodule import myfunction
tetapi kadang-kadang saya mendapatkan:
SystemError: Parent module '' not loaded, cannot perform relative import
Terkadang berhasil from mymodule import myfunction
, tetapi terkadang saya juga mendapatkan:
SystemError: Parent module '' not loaded, cannot perform relative import
Saya tidak mengerti logikanya di sini, dan saya tidak dapat menemukan penjelasan apa pun. Ini terlihat sangat acak.
Bisakah seseorang menjelaskan kepada saya apa logika di balik semua ini?
python
python-3.x
python-import
John Smith Opsional
sumber
sumber
Jawaban:
Cukup umum memiliki tata letak seperti ini ...
... dengan
mymodule.py
seperti ini ......
myothermodule.py
seperti ini ...... dan
main.py
seperti ini ...... yang berfungsi dengan baik saat Anda menjalankan
main.py
ataumypackage/mymodule.py
, tetapi gagal denganmypackage/myothermodule.py
, karena impor relatif ...Cara Anda menjalankannya adalah ...
... tapi agak verbose, dan tidak cocok dengan garis shebang seperti
#!/usr/bin/env python3
.Perbaikan paling sederhana untuk kasus ini, dengan asumsi nama
mymodule
itu unik secara global, akan menghindari menggunakan impor relatif, dan hanya menggunakan ...... walaupun, jika tidak unik, atau struktur paket Anda lebih kompleks, Anda harus memasukkan direktori yang berisi direktori paket Anda
PYTHONPATH
, dan melakukannya seperti ini ...... atau jika Anda ingin itu berfungsi "di luar kotak", Anda dapat menemukan
PYTHONPATH
kode in terlebih dahulu dengan ini ...Agak menyebalkan, tetapi ada petunjuk mengapa dalam email yang ditulis oleh Guido van Rossum tertentu ...
Apakah menjalankan skrip di dalam paket adalah antipattern atau tidak subjektif, tetapi secara pribadi saya merasa sangat berguna dalam paket yang saya miliki yang berisi beberapa widget wxPython kustom, jadi saya dapat menjalankan skrip untuk salah satu file sumber untuk menampilkan file yang
wx.Frame
berisi hanya berisi widget itu untuk tujuan pengujian.sumber
os.path.realpath(os.path.dirname(inspect.getfile(inspect.currentframe())))
olah Anda yakin bahwa modul Anda selalu layakfile
Anda juga bisa menggunakanos.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.
Saya tidak melihat bagaimana ini merupakan pola anti ... Sepertinya akan sangat nyaman jika impor relatif bekerja secara intuitif. Saya hanya ingin dapat mengimpor hal-hal yang saya tahu ada di direktori yang sama. Saya ingin tahu apa alasannyaPenjelasan
Dari PEP 328
Pada titik tertentu, PEP 338 bertentangan dengan PEP 328 :
dan untuk mengatasi masalah ini, PEP 366 memperkenalkan variabel tingkat atas
__package__
:(penekanan milikku)
Jika
__name__
adalah'__main__'
,__name__.rpartition('.')[0]
mengembalikan string kosong. Inilah sebabnya mengapa ada string kosong literal dalam deskripsi kesalahan:Bagian yang relevan dari CPython ini
PyImport_ImportModuleLevelObject
fungsi :CPython memunculkan pengecualian ini jika tidak dapat menemukan
package
(nama paket) diinterp->modules
(dapat diakses sebagaisys.modules
). Karenasys.modules
merupakan "kamus yang memetakan nama-nama modul untuk modul yang telah dimuat" , sekarang jelas bahwa modul orang tua harus secara eksplisit mutlak-impor sebelum melakukan impor relatif .Catatan: Patch dari masalah 18018 telah menambahkan blok lain
if
, yang akan dieksekusi sebelum kode di atas:Jika
package
(sama seperti di atas) adalah string kosong, pesan kesalahannya adalahNamun, Anda hanya akan melihat ini dalam Python 3.6 atau lebih baru.
Solusi # 1: Jalankan skrip Anda menggunakan -m
Pertimbangkan direktori (yang merupakan paket Python ):
Semua file dalam paket dimulai dengan 2 baris kode yang sama:
Saya menyertakan dua baris ini hanya untuk membuat urutan operasi jelas. Kami dapat mengabaikan mereka sepenuhnya, karena mereka tidak mempengaruhi eksekusi.
__init__.py dan module.py hanya berisi dua baris tersebut (yaitu, keduanya kosong secara efektif).
standalone.py juga mencoba mengimpor module.py melalui impor relatif:
Kami sadar bahwa itu
/path/to/python/interpreter package/standalone.py
akan gagal. Namun, kita dapat menjalankan modul dengan-m
opsi baris perintah yang akan "mencarisys.path
modul yang bernama dan mengeksekusi isinya sebagai__main__
modul" :-m
melakukan semua barang impor untuk Anda dan secara otomatis ditetapkan__package__
, tetapi Anda dapat melakukannya sendiri di menuSolusi # 2: Set __package__ secara manual
Harap perlakukan itu sebagai bukti konsep daripada solusi yang sebenarnya. Itu tidak cocok untuk digunakan dalam kode dunia nyata.
PEP 366 memiliki solusi untuk masalah ini, namun, itu tidak lengkap, karena pengaturan
__package__
saja tidak cukup. Anda akan perlu mengimpor setidaknya N paket sebelumnya dalam hierarki modul, tempat N adalah jumlah direktori induk (relatif terhadap direktori skrip) yang akan dicari untuk modul yang sedang diimpor.Jadi,
Tambahkan direktori induk dari Nth pendahulu modul saat ini
sys.path
Hapus direktori file saat ini dari
sys.path
Impor modul induk modul saat ini menggunakan nama yang sepenuhnya memenuhi syarat
Setel
__package__
ke nama yang sepenuhnya memenuhi syarat dari 2Lakukan impor relatif
Saya akan meminjam file dari Solusi # 1 dan menambahkan beberapa sub paket lagi:
Kali ini standalone.py akan mengimpor module.py dari paket paket menggunakan impor relatif berikut
Kita harus mendahului baris itu dengan kode boilerplate, untuk membuatnya bekerja.
Hal ini memungkinkan kita untuk mengeksekusi standalone.py dengan nama file:
Solusi yang lebih umum yang dibungkus dalam suatu fungsi dapat ditemukan di sini . Contoh penggunaan:
Solusi # 3: Gunakan impor absolut dan alat setup
Langkah-langkahnya adalah -
Ganti impor relatif eksplisit dengan impor absolut setara
Instal
package
untuk membuatnya dapat diimporSebagai contoh, struktur direktori mungkin sebagai berikut
di mana setup.py berada
Sisa file dipinjam dari Solusi # 1 .
Instalasi akan memungkinkan Anda untuk mengimpor paket terlepas dari direktori kerja Anda (dengan asumsi tidak akan ada masalah penamaan).
Kami dapat memodifikasi standalone.py untuk menggunakan keunggulan ini (langkah 1):
Ubah direktori kerja Anda
project
dan jalankan/path/to/python/interpreter setup.py install --user
(--user
instal paket di direktori paket situs Anda ) (langkah 2):Mari kita verifikasi bahwa sekarang mungkin untuk menjalankan standalone.py sebagai skrip:
Catatan : Jika Anda memutuskan untuk turun rute ini, Anda akan lebih baik menggunakan lingkungan virtual untuk menginstal paket secara terpisah.
Solusi # 4: Gunakan impor absolut dan beberapa kode boilerplate
Terus terang, instalasi tidak diperlukan - Anda dapat menambahkan beberapa kode boilerplate ke skrip Anda untuk membuat impor absolut berfungsi.
Saya akan meminjam file dari Solution # 1 dan mengubah standalone.py :
Menambahkan direktori induk dari paket untuk
sys.path
sebelum mencoba apa pun impor dari paket menggunakan impor absolut:Ganti impor relatif dengan impor absolut:
standalone.py berjalan tanpa masalah:
Saya merasa bahwa saya harus memperingatkan Anda: cobalah untuk tidak melakukan ini, terutama jika proyek Anda memiliki struktur yang kompleks.
Sebagai catatan tambahan, PEP 8 merekomendasikan penggunaan impor absolut, tetapi menyatakan bahwa dalam beberapa skenario, impor relatif eksplisit dapat diterima:
sumber
__package__
secara manual jika nama__main__
untuk menyelesaikan masalah?imp
modul dan mengatur__package__
sesuai tetapi hasilnya jelas anti-pola.AttributeError: 'PosixPath' object has no attribute 'path'
.Masukkan ini ke dalam file __init__.py paket Anda :
Dengan asumsi paket Anda seperti ini:
Sekarang gunakan impor reguler dalam paket Anda, seperti:
Ini berfungsi di kedua python 2 dan 3.
sumber
__init__.py
dasarnya akan menyelesaikan semua kesalahan impor relatif.sys.path
karena saya khawatir ini akan memengaruhi kode lain. (Sebagian ini karena saya tidak tahu seluk-beluk cara kerjanya.)Saya mengalami masalah ini. Solusi hack sedang mengimpor melalui blok if / else seperti berikut:
sumber
except:
itu buruk. gunakanexcept ImportError:
saja!SystemError
disini. (Py 3.4)if __name__ == '__main__': from mymod import as_int; else: from .mymod import as_int
.Mudah-mudahan, ini akan bernilai bagi seseorang di luar sana - saya telah melalui setengah lusin tumpukan stackoverflow mencoba untuk mencari tahu impor relatif mirip dengan apa yang diposting di atas sini. Saya mengatur semuanya seperti yang disarankan tetapi saya masih memukul
ModuleNotFoundError: No module named 'my_module_name'
Karena saya baru saja mengembangkan secara lokal dan bermain-main, saya belum membuat / menjalankan
setup.py
file. Saya juga belum mengaturnyaPYTHONPATH
.Saya menyadari bahwa ketika saya menjalankan kode saya ketika tes berada di direktori yang sama dengan modul, saya tidak dapat menemukan modul saya:
Namun, ketika saya secara spesifik menentukan jalur, segalanya mulai berfungsi:
Jadi, jika ada yang mencoba beberapa saran, yakin kode mereka terstruktur dengan benar dan masih menemukan diri mereka dalam situasi yang sama seperti saya mencoba salah satu dari berikut ini jika Anda tidak mengekspor direktori saat ini ke PYTHONPATH Anda:
$ PYTHONPATH=. python3 test/my_module/module_test.py
PYTHONPATH=.
, buatsetup.py
file dengan konten seperti berikut dan jalankanpython setup.py development
untuk menambahkan paket ke path:sumber
Saya perlu menjalankan python3 dari direktori proyek utama untuk membuatnya berfungsi.
Misalnya, jika proyek memiliki struktur berikut:
Larutan
Saya akan menjalankan python3 di dalam folder project_demo / dan kemudian melakukan a
sumber
Untuk mengatasi masalah ini, saya menemukan solusi dengan paket repackage , yang telah bekerja untuk saya selama beberapa waktu. Itu menambahkan direktori atas ke lib path:
Pengemasan ulang dapat membuat impor relatif yang bekerja dalam berbagai kasus, menggunakan strategi cerdas (memeriksa tumpukan panggilan).
sumber
jika kedua paket berada di jalur impor Anda (sys.path), dan modul / kelas yang Anda inginkan adalah contoh / example.py, maka untuk mengakses kelas tanpa impor relatif coba:
sumber
Saya pikir solusi terbaik adalah membuat paket untuk modul Anda: Di sini adalah info lebih lanjut tentang cara melakukannya.
Setelah Anda memiliki paket Anda tidak perlu khawatir tentang impor relatif, Anda bisa melakukan impor mutlak.
sumber
Saya memiliki masalah serupa: Saya membutuhkan layanan Linux dan plugin cgi yang menggunakan konstanta umum untuk bekerja sama. Cara 'alami' untuk melakukan ini adalah menempatkan mereka di init .py dari paket, tetapi saya tidak dapat memulai plugin cgi dengan parameter -m.
Solusi terakhir saya mirip dengan Solusi # 2 di atas:
Kerugiannya adalah Anda harus mengawali konstanta (atau fungsi umum) dengan pkg:
sumber