Saya telah menjawab pertanyaan tentang impor absolut dalam Python, yang saya pikir saya mengerti berdasarkan membaca changelog Python 2.5 dan menyertai PEP . Namun, setelah menginstal Python 2.5 dan mencoba membuat contoh penggunaan yang benar from __future__ import absolute_import
, saya menyadari hal-hal yang tidak begitu jelas.
Langsung dari changelog yang ditautkan di atas, pernyataan ini secara akurat merangkum pemahaman saya tentang perubahan impor absolut:
Katakanlah Anda memiliki direktori paket seperti ini:
pkg/ pkg/__init__.py pkg/main.py pkg/string.py
Ini mendefinisikan paket bernama
pkg
berisipkg.main
danpkg.string
submodules.Pertimbangkan kode dalam modul main.py. Apa yang terjadi jika mengeksekusi pernyataan
import string
? Dalam Python 2.4 dan sebelumnya, pertama-tama akan melihat direktori paket untuk melakukan impor relatif, menemukan pkg / string.py, mengimpor konten file itu sebagaipkg.string
modul, dan modul itu terikat dengan nama"string"
dipkg.main
namespace modul.
Jadi saya membuat struktur direktori yang tepat ini:
$ ls -R
.:
pkg/
./pkg:
__init__.py main.py string.py
__init__.py
dan string.py
kosong. main.py
berisi kode berikut:
import string
print string.ascii_uppercase
Seperti yang diharapkan, menjalankan ini dengan Python 2.5 gagal dengan AttributeError
:
$ python2.5 pkg/main.py
Traceback (most recent call last):
File "pkg/main.py", line 2, in <module>
print string.ascii_uppercase
AttributeError: 'module' object has no attribute 'ascii_uppercase'
Namun, lebih jauh di dalam changelog 2.5, kami menemukan ini (penekanan ditambahkan):
Dalam Python 2.5, Anda bisa mengubah
import
perilaku menjadi impor absolut menggunakanfrom __future__ import absolute_import
arahan. Perilaku impor absolut ini akan menjadi default di versi yang akan datang (mungkin Python 2.7). Setelah impor absolut adalah default,import string
akan selalu menemukan versi perpustakaan standar.
Dengan demikian saya dibuat pkg/main2.py
, identik dengan main.py
tetapi dengan arahan impor tambahan di masa depan. Sekarang terlihat seperti ini:
from __future__ import absolute_import
import string
print string.ascii_uppercase
Menjalankan ini dengan Python 2.5, namun ... gagal dengan AttributeError
:
$ python2.5 pkg/main2.py
Traceback (most recent call last):
File "pkg/main2.py", line 3, in <module>
print string.ascii_uppercase
AttributeError: 'module' object has no attribute 'ascii_uppercase'
Ini cukup bertentangan dengan pernyataan yang import string
akan selalu menemukan versi std-lib dengan impor absolut diaktifkan. Terlebih lagi, meskipun ada peringatan bahwa impor absolut dijadwalkan untuk menjadi perilaku "default baru", saya mengalami masalah yang sama menggunakan kedua Python 2.7, dengan atau tanpa __future__
arahan:
$ python2.7 pkg/main.py
Traceback (most recent call last):
File "pkg/main.py", line 2, in <module>
print string.ascii_uppercase
AttributeError: 'module' object has no attribute 'ascii_uppercase'
$ python2.7 pkg/main2.py
Traceback (most recent call last):
File "pkg/main2.py", line 3, in <module>
print string.ascii_uppercase
AttributeError: 'module' object has no attribute 'ascii_uppercase'
serta Python 3.5, dengan atau tanpa (dengan asumsi print
pernyataan diubah di kedua file):
$ python3.5 pkg/main.py
Traceback (most recent call last):
File "pkg/main.py", line 2, in <module>
print(string.ascii_uppercase)
AttributeError: module 'string' has no attribute 'ascii_uppercase'
$ python3.5 pkg/main2.py
Traceback (most recent call last):
File "pkg/main2.py", line 3, in <module>
print(string.ascii_uppercase)
AttributeError: module 'string' has no attribute 'ascii_uppercase'
Saya sudah menguji variasi lain ini. Alih-alih string.py
, saya telah membuat modul kosong - direktori bernama string
hanya berisi kosong __init__.py
- dan bukannya mengeluarkan impor dari main.py
, saya harus cd
ke pkg
dan menjalankan impor langsung dari REPL. Tidak satu pun dari variasi ini (atau kombinasi keduanya) yang mengubah hasil di atas. Saya tidak bisa mendamaikan ini dengan apa yang saya baca tentang __future__
petunjuk dan impor absolut.
Tampaknya bagi saya ini mudah dijelaskan oleh yang berikut (ini dari Python 2 docs tetapi pernyataan ini tetap tidak berubah dalam docs yang sama untuk Python 3):
sys.path
(...)
Seperti diinisialisasi pada startup program, item pertama dari daftar ini
path[0]
,, adalah direktori yang berisi skrip yang digunakan untuk memanggil juru bahasa Python. Jika direktori skrip tidak tersedia (mis. Jika juru bahasa dipanggil secara interaktif atau jika skrip dibaca dari input standar),path[0]
adalah string kosong, yang mengarahkan Python untuk mencari modul dalam direktori saat ini terlebih dahulu.
Jadi apa yang saya lewatkan? Mengapa __future__
pernyataan itu tampaknya tidak melakukan apa yang dikatakannya, dan apa resolusi dari kontradiksi antara dua bagian dokumentasi ini, serta antara perilaku yang digambarkan dan yang sebenarnya?
sumber
Jawaban:
Changelog adalah kata-kata sembarangan.
from __future__ import absolute_import
tidak peduli apakah sesuatu merupakan bagian dari perpustakaan standar, danimport string
tidak akan selalu memberi Anda modul perpustakaan standar dengan impor absolut aktif.from __future__ import absolute_import
berarti jika Andaimport string
, Python akan selalu mencaristring
modul tingkat atas , bukancurrent_package.string
. Namun, itu tidak mempengaruhi logika yang digunakan Python untuk memutuskan file apastring
modul. Saat kamu melakukanpkg/script.py
tidak terlihat seperti bagian dari paket ke Python. Mengikuti prosedur normal,pkg
direktori ditambahkan ke path, dan semua.py
file dalampkg
direktori terlihat seperti modul tingkat atas.import string
menemukanpkg/string.py
bukan karena itu melakukan impor relatif, tetapi karenapkg/string.py
tampaknya menjadi modul tingkat atasstring
. Fakta bahwa ini bukanstring
modul perpustakaan standar tidak muncul.Untuk menjalankan file sebagai bagian dari
pkg
paket, Anda bisa melakukannyaDalam hal ini,
pkg
direktori tidak akan ditambahkan ke path. Namun, direktori saat ini akan ditambahkan ke jalur.Anda juga dapat menambahkan beberapa boilerplate untuk
pkg/script.py
membuat Python memperlakukannya sebagai bagian daripkg
paket bahkan ketika dijalankan sebagai file:Namun, ini tidak akan mempengaruhi
sys.path
. Anda akan memerlukan penanganan tambahan untuk menghapuspkg
direktori dari path, dan jikapkg
direktori induk tidak ada di path, Anda juga harus tetap pada path tersebut.sumber
import string
jika Anda secara tidak sengaja membayangi, setidaknya tanpa riflingsys.modules
. Bukankah ini yangfrom __future__ import absolute_import
dimaksudkan untuk dicegah? Apa fungsinya? (PS, saya bukan downvoter.)sys.path
kerjanya, dan pertanyaan sebenarnya belum ditanggapi sama sekali. Yaitu, Apa yangfrom __future__ import absolute_import
sebenarnya dilakukan?sys.modules
tidak akan mendapatkan Andastring
modul perpustakaan standar jika Anda membayangi modul tingkat atas Anda sendiri.from __future__ import absolute_import
tidak dimaksudkan untuk menghentikan modul tingkat atas dari membayangi modul tingkat atas; itu seharusnya menghentikan modul internal paket dari membayangi modul tingkat atas. Jika Anda menjalankan file sebagai bagian daripkg
paket, file internal paket berhenti muncul sebagai tingkat atas.pkg
adalah paket di jalur pencarian impor, itu seharusnyapython -m pkg.main
.-m
membutuhkan nama modul, bukan jalur file.Perbedaan antara impor absolut dan relatif ikut bermain hanya ketika Anda mengimpor modul dari suatu paket dan modul itu mengimpor submodule lain dari paket itu. Lihat perbedaannya:
Khususnya:
Catatan yang
python2 pkg/main2.py
memiliki perilaku berbeda kemudian meluncurkanpython2
dan kemudian mengimporpkg.main2
(yang setara dengan menggunakan-m
sakelar).Jika Anda ingin menjalankan submodule suatu paket selalu gunakan
-m
switch yang mencegah interpreter untuk merantaisys.path
daftar dan dengan benar menangani semantik submodule.Juga, saya lebih suka menggunakan impor relatif eksplisit untuk submodul paket karena mereka memberikan lebih banyak semantik dan pesan kesalahan yang lebih baik jika terjadi kegagalan.
sumber
python2 pkg/main2.py
memiliki perilaku yang berbeda kemudian meluncurkan python2 dan kemudian mengimpor pkg.main2?pkg/main2.py
python (versi 2) tidak memperlakukanpkg
sebagai paket. Sementara menggunakanpython2 -m pkg.main2
atau mengimpor melakukan memperhitungkan bahwapkg
adalah sebuah paket.