Kamus diperintahkan dalam Python 3.6 (di bawah implementasi CPython setidaknya) tidak seperti dalam inkarnasi sebelumnya. Ini sepertinya perubahan besar, tetapi hanya paragraf pendek dalam dokumentasi . Ini digambarkan sebagai detail implementasi CPython daripada fitur bahasa, tetapi juga menyiratkan ini dapat menjadi standar di masa depan.
Bagaimana kinerja implementasi kamus baru lebih baik daripada yang lama sambil mempertahankan urutan elemen?
Ini adalah teks dari dokumentasi:
dict()
sekarang menggunakan representasi "kompak" yang dipelopori oleh PyPy . Penggunaan memori dict baru () adalah antara 20% dan 25% lebih kecil dibandingkan dengan Python 3.5. PEP 468 (Mempertahankan urutan ** kwargs dalam suatu fungsi.) Diimplementasikan oleh ini. Aspek pelestarian pesanan dari implementasi baru ini dianggap sebagai detail implementasi dan tidak boleh diandalkan (ini dapat berubah di masa depan, tetapi diharapkan memiliki implementasi dict baru ini dalam bahasa untuk beberapa rilis sebelum mengubah spesifikasi bahasa untuk mengamanatkan semantik pengawet pesanan untuk semua implementasi Python saat ini dan di masa depan, ini juga membantu menjaga kompatibilitas ke belakang dengan versi bahasa yang lebih lama di mana urutan iterasi acak masih berlaku, misalnya Python 3.5). (Disumbangkan oleh INADA Naoki dimasalah 27350 . Ide awalnya disarankan oleh Raymond Hettinger .)
Pembaruan Desember 2017: dict
pesanan penyisipan penahan dijamin untuk Python 3.7
sumber
**kwargs
dan karena itu kata-kata yang digunakan adalah diplomatik:**kwargs
dalam suatu fungsi tanda tangan sekarang dijamin menjadi pemetaan pelestarian urutan-penyisipan . Mereka telah menggunakan istilah pemetaan untuk tidak memaksa implementasi lain untuk membuat diktat dipesan (dan menggunakanOrderedDict
internal) dan sebagai cara untuk memberi sinyal bahwa ini tidak seharusnya tergantung pada kenyataan bahwadict
tidak dipesan.Jawaban:
Mereka memerintahkan penyisipan [1] . Pada Python 3.6, untuk implementasi Python CPython, kamus mengingat urutan item yang dimasukkan . Ini dianggap sebagai detail implementasi dalam Python 3.6 ; Anda perlu menggunakan
OrderedDict
jika Anda ingin pemesanan penyisipan yang dijamin di seluruh implementasi Python lainnya (dan perilaku berurutan lainnya [1] ).Pada Python 3.7 , ini bukan lagi detail implementasi dan malah menjadi fitur bahasa. Dari pesan python-dev oleh GvR :
Ini berarti bahwa Anda dapat bergantung padanya . Implementasi lain dari Python juga harus menawarkan penyisipan kamus jika mereka ingin menjadi implementasi yang sesuai dari Python 3.7.
Intinya, dengan menjaga dua array .
Array pertama
dk_entries
,, menampung entri ( dari jenisPyDictKeyEntry
) untuk kamus sesuai urutan yang dimasukkan. Order pelestarian dicapai dengan menjadi array append saja di mana item baru selalu disisipkan di akhir (urutan penyisipan).Yang kedua,,
dk_indices
memegang indeks untukdk_entries
array (yaitu, nilai-nilai yang menunjukkan posisi entri yang sesuai didk_entries
). Array ini bertindak sebagai tabel hash. Ketika kunci di-hash, itu mengarah ke salah satu indeks yang disimpandk_indices
dan entri yang sesuai diambil dengan mengindeksdk_entries
. Karena hanya indeks yang disimpan, jenis larik ini tergantung pada ukuran keseluruhan kamus (mulai dari jenisint8_t
(1
byte) hinggaint32_t
/int64_t
(4
/8
byte) di32
/64
bit builds)Dalam implementasi sebelumnya, berbagai jenis
PyDictKeyEntry
dan ukurandk_size
harus dialokasikan; Sayangnya, itu juga menghasilkan banyak ruang kosong karena array itu tidak boleh lebih dari2/3 * dk_size
penuh karena alasan kinerja . (dan ruang kosong masih memilikiPyDictKeyEntry
ukuran!).Ini bukan masalahnya sekarang karena hanya entri yang diperlukan yang disimpan (yang telah dimasukkan) dan jenis array yang jarang
intX_t
(X
tergantung pada ukuran dict)2/3 * dk_size
penuh disimpan. Ruang kosong berubah dari tipePyDictKeyEntry
keintX_t
.Jadi, jelas, membuat array tipe jarang
PyDictKeyEntry
jauh lebih banyak menuntut memori daripada array jarang untuk menyimpanint
s.Anda dapat melihat percakapan lengkap di Python-Dev mengenai fitur ini jika tertarik, ini adalah bacaan yang bagus.
Dalam proposal asli yang dibuat oleh Raymond Hettinger , visualisasi dari struktur data yang digunakan dapat dilihat yang menangkap inti gagasan.
Seperti yang dapat Anda lihat secara visual sekarang, dalam proposal asli, banyak ruang pada dasarnya kosong untuk mengurangi tabrakan dan membuat pencarian lebih cepat. Dengan pendekatan baru, Anda mengurangi memori yang dibutuhkan dengan menggerakkan sparseness di tempat yang benar-benar diperlukan, dalam indeks.
[1]: Saya katakan "penyisipan memerintahkan" dan tidak "memerintahkan" karena, dengan adanya OrderedDict, "memerintahkan" menunjukkan perilaku lebih lanjut bahwa
dict
objek tidak menyediakan . OrderedDicts bersifat reversibel, menyediakan metode sensitif pesanan dan, terutama, memberikan tes kesetaraan pesanan-luas (==
,!=
).dict
Saat ini tidak menawarkan perilaku / metode tersebut.[2]: Implementasi kamus baru menghasilkan ingatan yang lebih baik dengan dirancang lebih kompak; itulah manfaat utama di sini. Dari segi kecepatan, perbedaannya tidak terlalu drastis, ada tempat-tempat dict yang baru mungkin memperkenalkan sedikit kemunduran ( pencarian kunci, misalnya ) sementara di tempat lain (iterasi dan mengubah ukuran muncul dalam pikiran) peningkatan kinerja harus ada.
Secara keseluruhan, kinerja kamus, terutama dalam situasi kehidupan nyata, meningkat karena kekompakan yang diperkenalkan.
sumber
entries
daftar diubah ukurannya? atau ruang kosong disimpan? atau dikompresi dari waktu ke waktu?DKIX_DUMMY
dengan nilai-2
dan entri dalamentry
array digantikan olehNULL
, ketika memasukkan dilakukan nilai-nilai baru ditambahkan ke array entri, Belum bisa melihat, tapi cukup yakin ketika indeks terisi melebihi2/3
ambang batas dilakukan. Ini dapat menyebabkan penyusutan alih-alih bertambah jika banyakDUMMY
entri.d = {i:i for i in range(100)}
dan Anda.pop
semua item tanpa memasukkan, ukurannya tidak akan berubah. Ketika Anda menambahkannya lagi,d[1] = 1
ukuran yang sesuai dihitung dan ukurannya diubah.dict
dipesan',dict
s tidak dipesan dalam artiOrderedDict
s. Masalah yang menonjol adalah kesetaraan.dict
Ada pesanan tidak sensitif==
,OrderedDict
ada pesanan sensitif. MembuangOrderedDict
dan mengubahdicts
untuk sekarang memiliki perbandingan sensitif urutan dapat menyebabkan banyak kerusakan pada kode lama. Saya menduga satu-satunya hal yang mungkin berubah tentangOrderedDict
s adalah implementasinya.Di bawah ini menjawab pertanyaan pertama yang asli:
Saya pikir kalimat dari dokumentasi ini sebenarnya cukup untuk menjawab pertanyaan Anda
dict
tidak secara eksplisit dimaksudkan sebagai koleksi yang dipesan, jadi jika Anda ingin tetap konsisten dan tidak bergantung pada efek samping dari implementasi baru Anda harus tetap menggunakannyaOrderedDict
.Buat kode Anda menjadi bukti di masa depan :)
Ada perdebatan tentang itu di sini .
EDIT: Python 3.7 akan membuat ini sebagai fitur lihat
sumber
Pembaruan: Guido van Rossum mengumumkan di milis bahwa pada Python 3.7
dict
s di semua implementasi Python harus mempertahankan urutan penyisipan.sumber
move_to_end
metode dan kesetaraannya peka terhadap pesanan: docs.python.org/3/library/… . Lihat catatan pada jawaban Jim Fasarakis Hilliard.Saya ingin menambah diskusi di atas tetapi tidak memiliki reputasi untuk berkomentar.
Python 3.8 belum cukup dirilis, tetapi bahkan akan menyertakan
reversed()
fungsi pada kamus (menghapus perbedaan lain dariOrderedDict
.Saya tidak melihat penyebutan operator kesetaraan atau fitur lain
OrderedDict
sehingga mereka masih belum sepenuhnya sama.sumber