Tampaknya sudah ada beberapa pertanyaan di sini tentang impor relatif dalam python 3, tetapi setelah melalui banyak dari mereka saya masih belum menemukan jawaban untuk masalah saya. jadi inilah pertanyaannya.
Saya memiliki paket yang ditunjukkan di bawah ini
package/
__init__.py
A/
__init__.py
foo.py
test_A/
__init__.py
test.py
dan saya memiliki satu baris di test.py:
from ..A import foo
sekarang, saya ada di folder package
, dan saya jalankan
python -m test_A.test
Saya mendapat pesan
"ValueError: attempted relative import beyond top-level package"
tetapi jika saya berada di folder induk package
, misalnya, saya menjalankan:
cd ..
python -m package.test_A.test
semuanya baik-baik saja.
Sekarang pertanyaan saya adalah:
ketika saya berada di folder package
, dan saya menjalankan modul di dalam sub-paket test_A karena test_A.test
, berdasarkan pemahaman saya, ..A
naik hanya satu tingkat, yang masih di dalam package
folder, mengapa memberikan pesan yang mengatakan beyond top-level package
. Apa sebenarnya alasan yang menyebabkan pesan kesalahan ini?
Jawaban:
EDIT: Ada jawaban yang lebih baik / lebih koheren untuk pertanyaan ini dalam pertanyaan lain:
Mengapa itu tidak berhasil? Itu karena python tidak merekam dari mana sebuah paket diambil. Jadi ketika Anda melakukannya
python -m test_A.test
, itu pada dasarnya hanya membuang pengetahuan yangtest_A.test
sebenarnya disimpanpackage
(yaitupackage
tidak dianggap sebagai paket). Mencobafrom ..A import foo
mencoba mengakses informasi yang tidak ada lagi (mis. Direktori saudara dari lokasi yang dimuat). Ini konseptual mirip dengan memungkinkanfrom ..os import path
dalam file dimath
. Ini akan menjadi buruk karena Anda ingin paketnya berbeda. Jika mereka perlu menggunakan sesuatu dari paket lain, maka mereka harus merujuknya secara global denganfrom os import path
dan membiarkan python mencari tahu di mana itu dengan$PATH
dan$PYTHONPATH
.Ketika Anda menggunakan
python -m package.test_A.test
, maka gunakanfrom ..A import foo
resolves dengan baik karena itu melacak apa yang ada di dalampackage
dan Anda hanya mengakses direktori anak dari lokasi yang dimuat.Mengapa python tidak menganggap direktori kerja saat ini sebagai sebuah paket? TANPA PETUNJUK , tapi ampun itu akan berguna.
sumber
-m
flag dan menjalankan dari direktori di atas.sys.path
peretasan, tetapi penggunaan setuptools , yang jauh lebih menarik menurut saya.Coba ini. Bekerja untukku.
sumber
A/bar.py
ada dan dalam dirifoo.py
Andafrom .bar import X
.Asumsi:
Jika Anda berada di
package
direktori,A
dantest_A
merupakan paket terpisah.Kesimpulan:
..A
impor hanya diperbolehkan dalam satu paket.Catatan lebih lanjut:
Membuat impor relatif hanya tersedia dalam paket berguna jika Anda ingin memaksa agar paket dapat ditempatkan di jalur apa pun yang berada di
sys.path
.EDIT:
Direktori kerja saat ini biasanya terletak di sys.path. Jadi, semua file yang ada dapat diimpor. Ini adalah perilaku sejak Python 2 ketika paket belum ada. Membuat direktori yang sedang berjalan paket akan memungkinkan impor modul sebagai "impor .A" dan sebagai "impor A" yang kemudian akan menjadi dua modul yang berbeda. Mungkin ini adalah ketidakkonsistenan untuk dipertimbangkan.
sumber
python -m package.test_A.test
kelihatannya melakukan apa yang diinginkan, dan argumen saya adalah bahwa itu harus menjadi default. Jadi, dapatkah Anda memberi saya contoh ketidakkonsistenan ini?#include
akan sangat berguna!Tidak ada solusi ini yang berfungsi untuk saya di 3.6, dengan struktur folder seperti:
Tujuan saya adalah mengimpor dari module1 ke module2. Yang akhirnya berhasil bagi saya adalah, anehnya:
Perhatikan titik tunggal yang bertentangan dengan solusi dua titik yang disebutkan sejauh ini.
Sunting: Berikut ini membantu mengklarifikasi bagi saya:
Dalam kasus saya, direktori kerja (secara tidak terduga) adalah akar dari proyek.
sumber
sys.path.append(".")
berfungsi karena Anda memanggilnya di direktori induk, perhatikan bahwa.
selalu merupakan direktori tempat Anda menjalankan perintah python.from package.A import foo
Saya pikir itu lebih jelas daripada
sumber
sys.path.append("..")
. diuji pada python 3.6Seperti yang disarankan oleh jawaban paling populer, pada dasarnya itu karena Anda
PYTHONPATH
atausys.path
termasuk.
tetapi bukan jalur Anda ke paket Anda. Dan impor relatif relatif terhadap direktori kerja Anda saat ini, bukan file di mana impor terjadi; anehnyaAnda bisa memperbaikinya dengan terlebih dahulu mengubah impor relatif Anda menjadi absolut dan kemudian memulainya dengan:
ATAU memaksa jalur python ketika dipanggil dengan cara ini, karena:
Dengan
python -m test_A.test
Anda mengeksekusitest_A/test.py
dengan__name__ == '__main__'
dan__file__ == '/absolute/path/to/test_A/test.py'
Itu berarti bahwa di dalam
test.py
Anda dapat menggunakanimport
semi-dilindungi absolut dalam kondisi kasus utama dan juga melakukan manipulasi jalur Python satu kali:sumber
Sunting: 2020-05-08: Sepertinya situs web yang saya kutip tidak lagi dikendalikan oleh orang yang menulis saran, jadi saya menghapus tautan ke situs tersebut. Terima kasih telah memberi tahu saya baxx.
Jika seseorang masih berjuang sedikit setelah jawaban-jawaban hebat telah diberikan, saya menemukan saran di situs web yang tidak lagi tersedia.
Kutipan penting dari situs yang saya sebutkan:
Sudah cukup jelas bahwa harus seperti ini, memikirkannya setelah fakta. Saya mencoba menggunakan sys.path.append ('..') dalam pengujian saya, tetapi mengalami masalah yang diposting oleh OP. Dengan menambahkan impor dan definisi sys.path sebelum impor saya yang lain, saya bisa menyelesaikan masalah.
sumber
jika Anda memiliki
__init__.py
di folder atas, Anda dapat menginisialisasi impor sepertiimport file/path as alias
pada file init itu. Kemudian Anda dapat menggunakannya pada skrip yang lebih rendah sebagai:sumber
Menurut pendapat saya yang sederhana, saya memahami pertanyaan ini dengan cara ini:
[KASUS 1] Saat Anda memulai impor seperti absolut
atau
atau
Anda sebenarnya mengatur anchor impor menjadi
test_A
, dengan kata lain, paket tingkat atas adalahtest_A
. Jadi, ketika kami memiliki test.py lakukanfrom ..A import xxx
, Anda melarikan diri dari jangkar, dan Python tidak mengizinkan ini.[KASUS 2] Saat Anda melakukannya
atau
jangkar Anda menjadi
package
, jadipackage/test_A/test.py
melakukanfrom ..A import xxx
tidak lepas jangkar (masih di dalampackage
folder), dan Python dengan senang hati menerima ini.Pendeknya:
Selain itu, kami dapat menggunakan nama modul yang memenuhi syarat (FQMN) untuk memeriksa masalah ini.
Periksa FQMN dalam setiap kasus:
test.__name__
=package.test_A.test
test.__name__
=test_A.test
Jadi, untuk CASE2, sebuah
from .. import xxx
akan menghasilkan modul baru dengan FQMN =package.xxx
, yang dapat diterima.Sedangkan untuk CASE1,
..
dari dalamfrom .. import xxx
akan melompat keluar dari simpul awal (jangkar)test_A
, dan ini TIDAK diizinkan oleh Python.sumber
Tidak yakin dengan python 2.x tetapi dalam python 3.6, dengan asumsi Anda mencoba menjalankan seluruh rangkaian, Anda hanya perlu menggunakan
-t
Jadi, pada struktur suka
Misalnya seseorang dapat menggunakan:
python3 unittest discover -s /full_path/project_root/tests -t /full_path/project_root/
Dan masih mengimpor
my_module.my_class
tanpa drama utama.sumber