Saya pernah ke sini:
- http://www.python.org/dev/peps/pep-0328/
- http://docs.python.org/2/tutorial/modules.html#packages
- Paket python: impor relatif
- kode contoh impor relatif python tidak berfungsi
- Jawaban akhir untuk impor relatif python
- Impor relatif dengan Python
- Python: Menonaktifkan impor relatif
dan banyak URL yang tidak saya salin, beberapa di SO, beberapa di situs lain, ketika saya pikir saya akan mendapatkan solusinya dengan cepat.
Pertanyaan yang selalu berulang adalah ini: Dengan Windows 7, 32-bit Python 2.7.3, bagaimana cara saya menyelesaikan pesan "Upaya impor relatif bukan paket" ini? Saya membangun replika yang tepat dari paket di pep-0328:
package/
__init__.py
subpackage1/
__init__.py
moduleX.py
moduleY.py
subpackage2/
__init__.py
moduleZ.py
moduleA.py
Impor dilakukan dari konsol.
Saya memang membuat fungsi bernama spam dan telur dalam modul yang sesuai. Tentu, itu tidak berhasil. Jawabannya rupanya ada di URL ke-4 yang saya daftarkan, tetapi semuanya untuk saya. Ada tanggapan ini di salah satu URL yang saya kunjungi:
Impor relatif menggunakan atribut nama modul untuk menentukan posisi modul dalam hierarki paket. Jika nama modul tidak mengandung informasi paket apa pun (misalnya diatur ke 'utama') maka impor relatif diselesaikan seolah-olah modul tersebut adalah modul tingkat atas, terlepas dari di mana letak sebenarnya modul pada sistem file.
Tanggapan di atas terlihat menjanjikan, tetapi semua hieroglif bagi saya. Jadi pertanyaan saya, bagaimana cara membuat Python tidak kembali kepada saya "Mencoba impor relatif dalam paket non"? memiliki jawaban yang melibatkan -m, seharusnya.
Dapatkah seseorang tolong beri tahu saya mengapa Python memberikan pesan kesalahan itu, apa artinya dengan "non-paket", mengapa dan bagaimana Anda mendefinisikan 'paket', dan jawaban yang tepat cukup mudah dipahami oleh anak TK untuk mengerti .
sumber
def spam(): pass
, moduleA.py memilikidef eggs(): pass
. Saya mencoba mengeksekusi beberapa perintah "dari. Sesuatu impor", tetapi mereka tidak berhasil. Sekali lagi, lihat pep-0328.from .something import something
dalam interpreter interaktif, itu tidak akan berhasil. Impor relatif hanya dapat digunakan dalam modul, bukan secara interaktif.Jawaban:
Script vs. Modul
Berikut penjelasannya. Versi singkatnya adalah bahwa ada perbedaan besar antara langsung menjalankan file Python, dan mengimpor file itu dari tempat lain. Hanya mengetahui di mana direktori file tidak menentukan paket apa yang menurut Python masuk. Selain itu, tergantung pada bagaimana Anda memuat file ke Python (dengan menjalankan atau dengan mengimpor).
Ada dua cara untuk memuat file Python: sebagai skrip tingkat atas, atau sebagai modul. File dimuat sebagai skrip tingkat atas jika Anda menjalankannya secara langsung, misalnya dengan mengetik
python myfile.py
pada baris perintah. Itu dimuat sebagai modul jika Anda melakukannyapython -m myfile
, atau jika dimuat ketikaimport
pernyataan dijumpai di dalam beberapa file lain. Hanya ada satu skrip tingkat atas pada satu waktu; skrip tingkat atas adalah file Python yang Anda jalankan untuk memulai sesuatu.Penamaan
Ketika sebuah file dimuat, ia diberi nama (yang disimpan dalam
__name__
atributnya). Jika dimuat sebagai skrip tingkat atas, namanya adalah__main__
. Jika itu dimuat sebagai modul, namanya adalah nama file, didahului dengan nama paket / subpackages yang merupakan bagiannya, dipisahkan oleh titik-titik.Jadi misalnya dalam contoh Anda:
jika Anda mengimpor
moduleX
(catatan: diimpor , tidak langsung dieksekusi), namanya akan menjadipackage.subpackage1.moduleX
. Jika Anda mengimpormoduleA
, namanya akan menjadipackage.moduleA
. Namun, jika Anda langsung larimoduleX
dari baris perintah, namanya akan menjadi__main__
, dan jika Anda langsung larimoduleA
dari baris perintah, namanya akan menjadi__main__
. Ketika sebuah modul dijalankan sebagai skrip tingkat atas, ia kehilangan nama normalnya dan namanya adalah sebaliknya__main__
.Mengakses modul TIDAK melalui paketnya
Ada kerutan tambahan: nama modul tergantung pada apakah modul itu diimpor "langsung" dari direktori tempatnya, atau diimpor melalui paket. Ini hanya membuat perbedaan jika Anda menjalankan Python di direktori, dan mencoba mengimpor file di direktori yang sama (atau subdirektori dari itu). Misalnya, jika Anda memulai juru bahasa Python di direktori
package/subpackage1
dan kemudian melakukannyaimport moduleX
, namamoduleX
hanya akanmoduleX
, dan tidakpackage.subpackage1.moduleX
. Ini karena Python menambahkan direktori saat ini ke jalur pencarian saat startup; jika ia menemukan modul yang akan diimpor di direktori saat ini, ia tidak akan tahu bahwa direktori itu adalah bagian dari paket, dan informasi paket tidak akan menjadi bagian dari nama modul.Kasus khusus adalah jika Anda menjalankan interpreter secara interaktif (misalnya, cukup ketik
python
dan mulai memasukkan kode Python dengan cepat). Dalam hal ini nama sesi interaktif itu adalah__main__
.Sekarang di sini adalah hal penting untuk pesan kesalahan Anda: jika nama modul tidak memiliki titik, itu tidak dianggap sebagai bagian dari paket . Tidak masalah di mana file itu sebenarnya ada di disk. Yang penting adalah apa namanya, dan namanya tergantung pada bagaimana Anda memuatnya.
Sekarang lihat kutipan yang Anda masukkan dalam pertanyaan Anda:
Impor relatif ...
Impor relatif menggunakan modul nama untuk menentukan di mana itu adalah dalam sebuah paket. Saat Anda menggunakan impor relatif seperti
from .. import foo
, titik-titik menunjukkan untuk meningkatkan beberapa level dalam hirarki paket. Misalnya, jika nama modul Anda saat ini adalahpackage.subpackage1.moduleX
, maka..moduleA
itu berartipackage.moduleA
. Agarfrom .. import
dapat bekerja, nama modul harus memiliki setidaknya titik sebanyak yang ada dalamimport
pernyataan.... hanya relatif dalam satu paket
Namun, jika nama modul Anda
__main__
, itu tidak dianggap dalam sebuah paket. Namanya tidak memiliki titik, dan karena itu Anda tidak dapat menggunakanfrom .. import
pernyataan di dalamnya. Jika Anda mencoba melakukannya, Anda akan mendapatkan kesalahan "relatif-impor dalam paket".Skrip tidak dapat mengimpor relatif
Apa yang mungkin Anda lakukan adalah Anda mencoba menjalankan
moduleX
atau sejenisnya dari baris perintah. Ketika Anda melakukan ini, namanya ditetapkan ke__main__
, yang berarti bahwa impor relatif di dalamnya akan gagal, karena namanya tidak mengungkapkan bahwa itu ada dalam paket. Perhatikan bahwa ini juga akan terjadi jika Anda menjalankan Python dari direktori yang sama di mana modul berada, dan kemudian mencoba mengimpor modul itu, karena, seperti yang dijelaskan di atas, Python akan menemukan modul dalam direktori saat ini "terlalu dini" tanpa disadari. bagian dari paket.Juga ingat bahwa ketika Anda menjalankan juru bahasa interaktif, "nama" dari sesi interaktif itu selalu
__main__
. Dengan demikian Anda tidak dapat melakukan impor relatif langsung dari sesi interaktif . Impor relatif hanya untuk digunakan dalam file modul.Dua solusi:
Jika Anda benar-benar ingin menjalankan
moduleX
secara langsung, tetapi Anda tetap ingin itu dianggap sebagai bagian dari paket, Anda dapat melakukannyapython -m package.subpackage1.moduleX
. The-m
memberitahu Python untuk memuatnya sebagai modul, tidak seperti naskah tingkat atas.Atau mungkin Anda sebenarnya tidak ingin menjalankan
moduleX
, Anda hanya ingin menjalankan beberapa skrip lain, misalnyamyfile.py
, yang menggunakan fungsi di dalamnyamoduleX
. Jika demikian, letakkan dimyfile.py
tempat lain - tidak di dalampackage
direktori - dan jalankan. Jika di dalammyfile.py
Anda melakukan hal-hal sepertifrom package.moduleA import spam
, itu akan berfungsi dengan baik.Catatan
Untuk salah satu dari solusi ini, direktori paket (
package
dalam contoh Anda) harus dapat diakses dari jalur pencarian modul Python (sys.path
). Jika tidak, Anda tidak akan dapat menggunakan apa pun dalam paket dengan andal.Sejak Python 2.6, "nama" modul untuk keperluan resolusi paket ditentukan tidak hanya oleh
__name__
atributnya tetapi juga oleh__package__
atributnya. Itu sebabnya saya menghindari menggunakan simbol eksplisit__name__
untuk merujuk pada "nama" modul. Karena Python 2.6 modul "nama" modul secara efektif__package__ + '.' + __name__
, atau hanya__name__
jika__package__
adaNone
.)sumber
Its name has no dots, and therefore you cannot use from .. import statements inside it. If you try to do so, you will get the "relative-import in non-package" error.
Ini pada dasarnya mengganggu. Apa yang sulit dilihat dari direktori saat ini? Python harus mampu melakukan ini. Apakah ini diperbaiki dalam versi 3x?__package__ = 'package.subpackage1'
atau sejenisnya. Maka file itu hanya akan selalu dianggap sebagai bagian dari paket itu bahkan jika dijalankan secara langsung. Jika Anda memiliki pertanyaan lain tentang__package__
Anda, Anda mungkin ingin mengajukan pertanyaan terpisah karena kami akan keluar dari masalah pertanyaan awal Anda di sini.__name__
dansys.path
. Secara khusus, denganpython -m pkg.mod
,__name__
diatur ke__main__
, bukanpkg.mod
; impor relatif diselesaikan menggunakan__package__
daripada__name__
dalam kasus ini. Juga, Python menambahkan direktori skrip daripada direktori saat inisys.path
ketika menjalankanpython path/to/script.py
; itu menambah direktori saat inisys.path
ketika menjalankan sebagian besar cara lain, termasukpython -m pkg.mod
.Ini benar-benar masalah di dalam python. Asal mula kebingungan adalah bahwa orang secara keliru menganggap impor relatif sebagai jalur relatif yang bukan.
Misalnya ketika Anda menulis di faa.py :
Ini memiliki makna hanya jika faa.py itu diidentifikasi dan dimuat oleh python, selama eksekusi, sebagai bagian dari paket. Dalam hal itu, nama modul untuk faa.py akan menjadi contoh some_packagename.faa . Jika file dimuat hanya karena berada di direktori saat ini, ketika python dijalankan, maka namanya tidak akan merujuk ke paket apa pun dan akhirnya impor relatif akan gagal.
Solusi sederhana untuk merujuk modul di direktori saat ini, adalah dengan menggunakan ini:
sumber
from __future__ import absolute_import
dan memaksa pengguna untuk menggunakan kode Anda dengan benar ... sehingga Anda selalu dapat melakukannyafrom . import foo
Berikut adalah resep umum, dimodifikasi agar sesuai sebagai contoh, yang saya gunakan saat ini untuk berurusan dengan pustaka Python yang ditulis sebagai paket, yang berisi file-file yang saling tergantung, di mana saya ingin dapat menguji sebagian dari mereka sedikit demi sedikit. Mari panggil ini
lib.foo
dan katakan bahwa itu membutuhkan akses kelib.fileA
untuk fungsif1
danf2
, danlib.fileB
untuk kelasClass3
.Saya telah menyertakan beberapa
print
panggilan untuk membantu menggambarkan cara kerjanya. Dalam praktiknya Anda ingin menghapusnya (dan mungkin jugafrom __future__ import print_function
baris).Contoh khusus ini terlalu sederhana untuk ditampilkan ketika kita benar-benar perlu memasukkan entri
sys.path
. (Lihat jawaban Lars untuk kasus di mana kami benar- benar membutuhkannya, ketika kami memiliki dua atau lebih direktori paket, dan kemudian kami menggunakanos.path.dirname(os.path.dirname(__file__))
—tetapi tidak ada salahnya juga di sini.) Juga cukup aman untuk melakukan ini tanpaif _i in sys.path
uji. Namun, jika masing-masing file yang diimpor menyisipkan jalur yang sama — misalnya, jika keduanyafileA
danfileB
ingin mengimpor utilitas dari paket — inisys.path
berulang kali dengan jalur yang sama, jadi ada baiknyaif _i not in sys.path
di dalam boilerplate.Idenya di sini adalah ini (dan perhatikan bahwa semua ini berfungsi sama di python2.7 dan python 3.x):
Jika dijalankan sebagai
import lib
ataufrom lib import foo
sebagai paket reguler, impor dari kode biasa,__package
adalahlib
dan__name__
sekaranglib.foo
. Kami mengambil jalur kode pertama, mengimpor dari.fileA
, dll.Jika dijalankan sebagai
python lib/foo.py
,__package__
akan menjadi None dan__name__
akan menjadi__main__
.Kami mengambil jalur kode kedua. The
lib
direktori yang sudah akan berada disys.path
sehingga tidak perlu untuk menambahkannya. Kami mengimpor darifileA
, dll.Jika dijalankan dalam
lib
direktori sebagaipython foo.py
, perilaku ini sama seperti untuk kasus 2.Jika dijalankan dalam
lib
direktori sebagaipython -m foo
, perilaku ini mirip dengan kasus 2 dan 3. Namun, jalur kelib
direktori tidak adasys.path
, jadi kami menambahkannya sebelum mengimpor. Hal yang sama berlaku jika kita menjalankan Python laluimport foo
.(Karena
.
sudah di dalamsys.path
, kita tidak benar-benar perlu menambahkan versi absolut dari path di sini. Di sinilah struktur paket bersarang yang lebih dalam, di mana kita ingin lakukanfrom ..otherlib.fileC import ...
, membuat perbedaan. Jika Anda tidak melakukan ini, Anda dapat hilangkan semuasys.path
manipulasi sepenuhnya.)Catatan
Masih ada kekhasan. Jika Anda menjalankan semua ini dari luar:
atau:
perilaku tergantung pada isi
lib/__init__.py
. Jika itu ada dan kosong , semuanya baik-baik saja:Tetapi jika
lib/__init__.py
itu sendiri imporroutine
sehingga dapat mengeksporroutine.name
langsunglib.name
, Anda mendapatkan:Yaitu, modul akan diimpor dua kali, sekali melalui paket dan kemudian lagi
__main__
sehingga menjalankanmain
kode Anda . Python 3.6 dan yang lebih baru memperingatkan tentang ini:The peringatan baru, tapi perilaku memperingatkan-tentang tidak. Ini adalah bagian dari apa yang oleh beberapa orang disebut perangkap impor ganda . (Untuk perincian tambahan, lihat edisi 27487. ) Nick Coghlan mengatakan:
Perhatikan bahwa sementara kami melanggar aturan itu di sini, kami melakukannya hanya ketika file yang sedang dimuat tidak dimuat sebagai bagian dari paket, dan modifikasi kami dirancang khusus untuk memungkinkan kami mengakses file lain dalam paket itu. (Dan, seperti yang saya catat, kita mungkin tidak boleh melakukan ini sama sekali untuk paket tingkat tunggal.) Jika kita ingin menjadi ekstra bersih, kita mungkin menulis ulang ini sebagai, misalnya:
Yaitu, kami memodifikasi
sys.path
cukup lama untuk mencapai impor kami, lalu mengembalikannya seperti semula (menghapus satu salinan_i
jika dan hanya jika kami menambahkan satu salinan_i
).sumber
Jadi setelah memikirkan hal ini bersama dengan banyak orang lain, saya menemukan catatan yang diposting oleh Dorian B dalam artikel ini yang memecahkan masalah spesifik yang saya alami di mana saya akan mengembangkan modul dan kelas untuk digunakan dengan layanan web, tetapi saya juga ingin menjadi dapat mengujinya saat saya mengode, menggunakan fasilitas debugger di PyCharm. Untuk menjalankan tes di kelas mandiri, saya akan menyertakan yang berikut di akhir file kelas saya:
tetapi jika saya ingin mengimpor kelas atau modul lain dalam folder yang sama, saya kemudian harus mengubah semua pernyataan impor saya dari notasi relatif ke referensi lokal (yaitu menghapus titik (.)) Tetapi setelah membaca saran Dorian, saya mencoba nya ' satu-liner 'dan berhasil! Saya sekarang dapat menguji di PyCharm dan meninggalkan kode tes saya di tempat ketika saya menggunakan kelas di kelas lain yang diuji, atau ketika saya menggunakannya di layanan web saya!
Pernyataan if memeriksa untuk melihat apakah kita menjalankan modul ini sebagai utama atau jika sedang digunakan dalam modul lain yang sedang diuji sebagai utama . Mungkin ini jelas, tetapi saya menawarkan catatan ini di sini kalau-kalau ada orang lain yang frustrasi dengan masalah impor relatif di atas dapat memanfaatkannya.
sumber
Berikut adalah satu solusi yang tidak akan saya rekomendasikan, tetapi mungkin berguna dalam beberapa situasi di mana modul tidak dihasilkan:
sumber
Saya memiliki masalah yang sama di mana saya tidak ingin mengubah jalur pencarian modul Python dan perlu memuat modul relatif dari skrip (terlepas dari "skrip tidak dapat mengimpor relatif dengan semua" seperti yang dijelaskan oleh BrenBarn dengan baik di atas).
Jadi saya menggunakan hack berikut. Sayangnya, itu bergantung pada
imp
modul yang menjadi usang sejak versi 3.4 yang akan dibatalkanimportlib
. (Apakah ini mungkin denganimportlib
juga? Saya tidak tahu.) Namun, peretasan bekerja untuk saat ini.Contoh untuk mengakses anggota
moduleX
insubpackage1
dari skrip yang berada disubpackage2
folder:Pendekatan yang lebih bersih adalah memodifikasi sys.path yang digunakan untuk memuat modul seperti yang disebutkan oleh Federico.
sumber
__name__
perubahan tergantung pada apakah kode tersebut dijalankan di namespace global atau sebagai bagian dari modul yang diimpor.Jika kode tidak berjalan di ruang global,
__name__
akan menjadi nama modul. Jika sedang berjalan di namespace global - misalnya, jika Anda mengetiknya di konsol, atau menjalankan modul sebagai skrip menggunakanpython.exe yourscriptnamehere.py
maka__name__
menjadi"__main__"
.Anda akan melihat banyak kode python
if __name__ == '__main__'
digunakan untuk menguji apakah kode tersebut dijalankan dari namespace global - yang memungkinkan Anda memiliki modul yang berfungsi ganda sebagai skrip.Apakah Anda mencoba melakukan impor ini dari konsol?
sumber
@ BrenBarn menjawab dengan mengatakan itu semua, tetapi jika Anda seperti saya mungkin perlu waktu untuk mengerti. Inilah kasus saya dan bagaimana jawaban @ BrenBarn berlaku untuk itu, mungkin ini akan membantu Anda.
Kasus
Menggunakan contoh yang kita kenal, dan menambahkan bahwa moduleX.py memiliki impor relatif ke ..moduleA. Mengingat bahwa saya mencoba menulis skrip pengujian di direktori subpackage1 yang mengimpor moduleX, tetapi kemudian mendapatkan kesalahan yang menakutkan yang dijelaskan oleh OP.
Larutan
Pindahkan skrip pengujian ke level yang sama dengan paket dan impor package.subpackage1.moduleX
Penjelasan
Seperti yang dijelaskan, impor relatif dibuat relatif terhadap nama saat ini. Ketika skrip pengujian saya mengimpor moduleX dari direktori yang sama, maka nama modul di dalam moduleX adalah moduleX. Ketika bertemu dengan impor relatif interpreter tidak dapat membuat cadangan hierarki paket karena sudah di atas
Ketika saya mengimpor moduleX dari atas, maka nama di dalam moduleX adalah package.subpackage1.moduleX dan impor relatif dapat ditemukan
sumber
Menulis paket python kecil ke PyPi yang mungkin membantu pemirsa pertanyaan ini. Paket ini bertindak sebagai solusi jika seseorang ingin dapat menjalankan file python yang mengandung impor yang berisi paket tingkat atas dari dalam suatu paket / proyek tanpa langsung di direktori file impor. https://pypi.org/project/import-anywhere/
sumber
Untuk membuat Python tidak kembali kepada saya "Mencoba impor relatif dalam paket non". paket/
init .py subpackage1 / init .py moduleX.py moduleY.py subpackage2 / init .py moduleZ.py moduleA.py
Kesalahan ini terjadi hanya jika Anda menerapkan impor relatif ke file induk. Misalnya file induk sudah kembali utama setelah Anda kode "cetak ( nama )" di moduleA.py. Jadi file INI sudah utamatidak dapat mengembalikan paket induk lebih lanjut. impor relatif diperlukan dalam file paket subpackage1 dan subpackage2 Anda dapat menggunakan ".." untuk merujuk ke direktori atau modul induk. Tetapi orangtua adalah jika sudah paket tingkat atas, ia tidak dapat melangkah lebih jauh di atas direktori induk (paket). File seperti itu di mana Anda menerapkan impor relatif ke orang tua hanya dapat bekerja dengan aplikasi impor absolut. Jika Anda akan menggunakan IMPOR MUTLAK DALAM PAKET ORANGTUA TIDAK ADA KESALAHAN yang akan terjadi karena python tahu siapa yang berada di tingkat atas paket bahkan jika file Anda berada dalam subkemasan karena konsep PYTHON PATH yang menentukan tingkat atas proyek.
sumber