Saya telah menggunakan Python semakin banyak, dan saya terus melihat __all__
set variabel dalam __init__.py
file yang berbeda . Adakah yang bisa menjelaskan hal ini?
python
syntax
namespaces
varikin
sumber
sumber
__all__
jika__all__
ada, tidak sepenuhnya tersembunyi; mereka dapat dilihat dan diakses dengan normal jika Anda tahu namanya. Hanya dalam kasus "impor *", yang tidak dianjurkan, perbedaannya memiliki bobot.import *
(seperti misalnyatk
). Petunjuk yang bagus jika hal ini terjadi adalah keberadaan__all__
atau nama yang dimulai dengan garis bawah pada kode modul.tk
dirilis hari ini (atau pada 2012, bahkan), praktik yang disarankan adalah menggunakanfrom tk import *
. Saya pikir praktik ini diterima karena inersia, bukan desain yang disengaja.Tertaut ke, tetapi tidak secara eksplisit disebutkan di sini, adalah persis kapan
__all__
digunakan. Ini adalah daftar string yang mendefinisikan simbol apa dalam modul yang akan diekspor saatfrom <module> import *
digunakan pada modul.Misalnya, kode berikut
foo.py
secara eksplisit mengekspor simbolbar
danbaz
:Simbol-simbol ini kemudian dapat diimpor seperti:
Jika di
__all__
atas dikomentari, kode ini kemudian akan dieksekusi sampai selesai, karena perilaku defaultimport *
adalah mengimpor semua simbol yang tidak dimulai dengan garis bawah, dari namespace yang diberikan.Referensi: https://docs.python.org/tutorial/modules.html#importing-from-a-package
CATATAN: hanya
__all__
mempengaruhifrom <module> import *
perilaku. Anggota yang tidak disebutkan dalam__all__
masih dapat diakses dari luar modul dan dapat diimpor denganfrom <module> import <member>
.sumber
print(baz())
?print(baz)
mencetak sesuatu seperti cetakan<function baz at 0x7f32bc363c10>
sementaraprint(baz())
baz
Apa yang
__all__
harus dilakukanIni mendeklarasikan nama "publik" semantik dari sebuah modul. Jika ada nama di
__all__
, pengguna diharapkan untuk menggunakannya, dan mereka dapat memiliki harapan bahwa itu tidak akan berubah.Itu juga akan memiliki dampak terprogram:
import *
__all__
dalam sebuah modul, misalnyamodule.py
:berarti bahwa ketika Anda
import *
dari modul, hanya nama-nama dalam__all__
yang diimpor:Alat dokumentasi
Alat dokumentasi dan pelengkapan otomatis kode dapat (pada kenyataannya, harus) juga memeriksa
__all__
untuk menentukan nama yang akan ditampilkan sebagai tersedia dari modul.__init__.py
membuat direktori paket PythonDari dokumen :
Jadi
__init__.py
dapat mendeklarasikan__all__
untuk paket .Mengelola API:
Paket biasanya terdiri dari modul yang dapat mengimpor satu sama lain, tetapi yang harus diikat bersama dengan
__init__.py
file. File itu yang membuat direktori paket Python yang sebenarnya. Misalnya, Anda memiliki file berikut dalam satu paket:Mari kita buat file-file ini dengan Python sehingga Anda dapat mengikuti - Anda dapat menempelkan yang berikut ke dalam shell Python 3:
Dan sekarang Anda telah menyajikan api lengkap yang dapat digunakan orang lain saat mereka mengimpor paket Anda, seperti:
Dan paket tidak akan memiliki semua detail implementasi lain yang Anda gunakan saat membuat modul Anda mengacaukan
package
namespace.__all__
di__init__.py
Setelah lebih banyak pekerjaan, mungkin Anda telah memutuskan bahwa modul terlalu besar (seperti ribuan baris?) Dan perlu dipisah. Jadi, Anda melakukan hal berikut:
Pertama-tama buat direktori subpackage dengan nama yang sama dengan modul:
Pindahkan implementasinya:
buat
__init__.py
s untuk sub paket yang menyatakan__all__
untuk masing-masing:Dan sekarang Anda masih memiliki api yang disediakan di tingkat paket:
Dan Anda dapat dengan mudah menambahkan hal-hal ke API Anda yang dapat Anda kelola di tingkat sub-paket bukan tingkat modul sub-paket. Jika Anda ingin menambahkan nama baru ke API, Anda cukup memperbarui
__init__.py
, misalnya di module_2:Dan jika Anda belum siap untuk mempublikasikan
Baz
di API tingkat atas, di level teratas__init__.py
Anda, Anda dapat memiliki:dan jika pengguna Anda mengetahui ketersediaan
Baz
, mereka dapat menggunakannya:tetapi jika mereka tidak mengetahuinya, alat lain (seperti pydoc ) tidak akan memberi tahu mereka.
Anda nanti dapat mengubahnya saat
Baz
siap untuk prime time:Awalan
_
versus__all__
:Secara default, Python akan mengekspor semua nama yang tidak dimulai dengan
_
. Anda tentu bisa mengandalkan mekanisme ini. Beberapa paket di pustaka standar Python, pada kenyataannya, memang mengandalkan ini, tetapi untuk melakukannya, mereka alias impor mereka, misalnya, dictypes/__init__.py
:Menggunakan
_
konvensi dapat menjadi lebih elegan karena menghilangkan redundansi penamaan nama lagi. Tetapi itu menambah redundansi untuk impor (jika Anda memiliki banyak impor) dan mudah untuk melupakan hal ini secara konsisten - dan hal terakhir yang Anda inginkan adalah harus tanpa batas mendukung sesuatu yang Anda maksudkan hanya menjadi detail implementasi, hanya saja karena Anda lupa untuk awalan_
ketika memberi nama fungsi.Saya pribadi menulis
__all__
awal dalam siklus hidup pengembangan saya untuk modul sehingga orang lain yang mungkin menggunakan kode saya tahu apa yang harus mereka gunakan dan tidak gunakan.Sebagian besar paket di perpustakaan standar juga digunakan
__all__
.Saat menghindar
__all__
masuk akalMasuk akal untuk tetap berpegang pada
_
konvensi awalan sebagai pengganti__all__
ketika:sebuah
export
dekoratorKelemahan dari penggunaan
__all__
adalah bahwa Anda harus menulis nama fungsi dan kelas yang diekspor dua kali - dan informasi disimpan terpisah dari definisi. Kita bisa menggunakan dekorator untuk menyelesaikan masalah ini.Saya mendapat ide untuk dekorator ekspor dari pembicaraan David Beazley tentang pengemasan. Implementasi ini tampaknya bekerja dengan baik di importir tradisional CPython. Jika Anda memiliki kait atau sistem impor khusus, saya tidak menjaminnya, tetapi jika Anda mengadopsinya, itu cukup sepele untuk mundur - Anda hanya perlu menambahkan nama secara manual ke dalam
__all__
Jadi di, misalnya, perpustakaan utilitas, Anda akan mendefinisikan dekorator:
dan kemudian, di mana Anda akan mendefinisikan
__all__
, Anda melakukan ini:Dan ini berfungsi baik apakah dijalankan sebagai utama atau diimpor oleh fungsi lain.
Dan penyediaan API dengan
import *
akan bekerja juga:sumber
@export
dekorator.__init__.py
dan penggunaan__all__
__all__
itu benar.__all__
juga - tapi kemudian saya akan mengatakan Anda memiliki API tidak stabil ... Ini akan menjadi sesuatu untuk memiliki beberapa tes penerimaan yang komprehensif.module_1
danmodule_2
; apakah boleh memasukkan eksplisitdel module_1
dalam__init__.py
? Apakah saya salah menganggap ini berharga?Saya hanya menambahkan ini tepatnya:
Semua jawaban lain merujuk pada modul . Pertanyaan asli secara eksplisit disebutkan
__all__
dalam__init__.py
file, jadi ini tentang paket python .Umumnya,
__all__
hanya berlaku ketikafrom xxx import *
varianimport
pernyataan digunakan. Ini berlaku untuk paket dan juga untuk modul.Perilaku untuk modul dijelaskan dalam jawaban lain. Perilaku yang tepat untuk paket dijelaskan di sini secara rinci.
Singkatnya,
__all__
pada tingkat paket melakukan hal yang kira-kira sama dengan untuk modul, kecuali itu berkaitan dengan modul dalam paket (berbeda dengan menentukan nama-nama dalam modul ). Jadi__all__
tentukan semua modul yang akan dimuat dan diimpor ke namespace saat ini saat kami gunakanfrom package import *
.Perbedaan besar adalah, bahwa ketika Anda menghilangkan deklarasi
__all__
dalam sebuah paket__init__.py
, pernyataanfrom package import *
itu tidak akan mengimpor apa pun (dengan pengecualian yang dijelaskan dalam dokumentasi, lihat tautan di atas).Di sisi lain, jika Anda menghilangkan
__all__
dalam sebuah modul, "impor berbintang" akan mengimpor semua nama (tidak dimulai dengan garis bawah) yang ditentukan dalam modul.sumber
from package import *
masih akan mengimpor semua yang didefinisikan__init__.py
, bahkan jika tidak adaall
. Perbedaan penting adalah bahwa tanpa__all__
itu tidak akan secara otomatis mengimpor modul yang ditentukan dalam direktori paket.Itu juga mengubah apa yang akan ditampilkan pydoc:
module1.py
module2.py
$ pydoc module1
$ pydoc module2
Saya menyatakan
__all__
dalam semua modul saya, serta menggarisbawahi detail internal, ini sangat membantu ketika menggunakan hal-hal yang belum pernah Anda gunakan sebelumnya dalam sesi penerjemah langsung.sumber
__all__
menyesuaikan*
difrom <module> import *
__all__
menyesuaikan*
difrom <package> import *
Sebuah modul adalah
.py
file yang dimaksudkan untuk diimpor.Sebuah paket adalah direktori dengan
__init__.py
file yang. Paket biasanya berisi modul.MODUL
__all__
memungkinkan manusia mengetahui fitur "publik" dari sebuah modul . [ @AaronHall ] Juga, pydoc mengenali mereka. [ @Longpoke ]dari impor modul *
Lihat bagaimana
swiss
dancheddar
dibawa ke ruang nama lokal, tetapi tidakgouda
:Tanpa
__all__
, simbol apa pun (yang tidak dimulai dengan garis bawah) akan tersedia.Impor tanpa
*
tidak terpengaruh oleh__all__
modul impor
dari nama impor modul
impor modul sebagai localname
PAKET
Dalam
__init__.py
file paket__all__
adalah daftar string dengan nama-nama modul publik atau objek lainnya. Fitur-fitur tersebut tersedia untuk impor wildcard. Seperti halnya modul,__all__
sesuaikan*
kapan wildcard mengimpor dari paket. [ @MartinStettner ]Berikut ini kutipan dari Konektor Python MySQL
__init__.py
:Kasing standar, tanda bintang tanpa
__all__
paket , rumit, karena perilaku yang jelas akan mahal: menggunakan sistem file untuk mencari semua modul dalam paket. Sebaliknya, dalam membaca dokumen saya, hanya objek yang didefinisikan__init__.py
diimpor:[ PEP 8 , @ToolmakerSteve]
sumber
from <package> import *
tanpa__all__
di__init__.py
yang tidak mengimpor modul .__init__.py
modul . Tapi saya tidak yakin itu akurat, atau khususnya jika objek garis bawah-awalan dikecualikan. Juga, saya lebih jelas memisahkan bagian tentang MODUL dan PAKET. Pikiran Anda?Dari Wiki Referensi Python (An Tidak Resmi) :
sumber
__all__
digunakan untuk mendokumentasikan API publik dari modul Python. Meskipun bersifat opsional,__all__
harus digunakan.Berikut adalah kutipan yang relevan dari referensi bahasa Python :
PEP 8 menggunakan kata-kata yang serupa, meskipun juga menjelaskan bahwa nama yang diimpor bukan bagian dari API publik ketika
__all__
tidak ada:Selanjutnya, seperti yang ditunjukkan dalam jawaban lain,
__all__
digunakan untuk mengaktifkan wildcard mengimpor untuk paket :sumber
Jawaban singkat
__all__
memengaruhifrom <module> import *
pernyataan.Jawaban panjang
Pertimbangkan contoh ini:
Dalam
foo/__init__.py
:(Tersirat) Jika kami tidak mendefinisikan
__all__
, makafrom foo import *
hanya akan mengimpor nama yang didefinisikan dalamfoo/__init__.py
.(Eksplisit) Jika kita mendefinisikan
__all__ = []
, maka tidakfrom foo import *
akan mengimpor apa pun.(Eksplisit) Jika kita mendefinisikan
__all__ = [ <name1>, ... ]
, makafrom foo import *
hanya akan mengimpor nama-nama itu.Perhatikan bahwa dalam kasus tersirat, python tidak akan mengimpor nama yang dimulai dengan
_
. Namun, Anda dapat memaksa mengimpor nama tersebut menggunakan__all__
.Anda dapat melihat dokumen Python di sini .
sumber
__all__
mempengaruhi carafrom foo import *
kerjanya.Kode yang ada di dalam tubuh modul (tetapi tidak di tubuh fungsi atau kelas) dapat menggunakan tanda bintang (
*
) dalamfrom
pernyataan:The
*
permintaan bahwa semua atribut modulfoo
(kecuali yang awal dengan garis bawah) terikat sebagai variabel global dalam modul mengimpor. Ketikafoo
memiliki atribut__all__
, nilai atribut adalah daftar nama-nama yang terikat oleh jenisfrom
pernyataan ini.Jika
foo
sebuah paket dan__init__.py
mendefinisikan daftar bernama__all__
, itu diambil menjadi daftar nama submodule yang harus diimpor ketikafrom foo import *
ditemui. Jika__all__
tidak didefinisikan, pernyataanfrom foo import *
mengimpor nama apa pun yang didefinisikan dalam paket. Ini termasuk nama apa pun yang didefinisikan (dan submodul yang dimuat secara eksplisit) oleh__init__.py
.Catatan yang
__all__
tidak harus menjadi daftar. Sesuai dokumentasi padaimport
pernyataan , jika ditentukan,__all__
harus berupa urutan string yang merupakan nama yang didefinisikan atau diimpor oleh modul. Jadi, Anda sebaiknya menggunakan tuple untuk menghemat beberapa siklus memori dan CPU. Hanya saja, jangan lupa koma jika modul mendefinisikan satu nama publik:Lihat juga Mengapa “impor *” buruk?
sumber
Ini didefinisikan dalam PEP8 di sini :
PEP8 menyediakan konvensi pengkodean untuk kode Python yang terdiri dari pustaka standar dalam distribusi Python utama. Semakin banyak Anda mengikuti ini, semakin dekat Anda dengan maksud aslinya.
sumber