Mengapa Python tidak mengatur perintah penyisipan?

12

Saya terkejut menemukan baru-baru ini bahwa sementara dikt dijamin untuk menjaga urutan penyisipan dengan Python 3.7+, set tidak:

>>> d = {'a': 1, 'b': 2, 'c': 3}
>>> d
{'a': 1, 'b': 2, 'c': 3}
>>> d['d'] = 4
>>> d
{'a': 1, 'b': 2, 'c': 3, 'd': 4}
>>> s = {'a', 'b', 'c'}
>>> s
{'b', 'a', 'c'}
>>> s.add('d')
>>> s
{'d', 'b', 'a', 'c'}

Apa alasan untuk perbedaan ini? Apakah peningkatan efisiensi yang sama yang menyebabkan tim Python mengubah implementasi dikt tidak berlaku untuk set juga?

Saya tidak mencari petunjuk untuk implementasi yang dipesan-set atau cara untuk menggunakan dikt sebagai stand-in untuk set. Saya hanya ingin tahu mengapa tim Python tidak membuat set built-in menjaga ketertiban pada saat yang sama mereka melakukannya untuk dikte.

Bart Robinson
sumber
1
Apakah ini menjawab pertanyaan Anda? Apakah Python memiliki set yang dipesan?
Mihai Chelaru
1
Tidak, saya mengerti bahwa Python tidak memiliki set yang sudah terpasang. Saya hanya bertanya-tanya mengapa demikian, karena dikte sekarang dipesan.
Bart Robinson
4
Pola penggunaannya berbeda, sehingga dioptimalkan untuk berbagai kasus penggunaan. Ini adalah kesalahpahaman umum bahwa set hanya dikte dengan nilai nol di CPython, itu sepenuhnya salah: implementasinya berbeda. Jika pertanyaan Anda tidak ditutup, saya dapat memposting jawaban terperinci.
wim
1
"Pola penggunaannya berbeda, jadi mereka dioptimalkan untuk berbagai kasus penggunaan." Jawaban yang bagus untuk pertanyaan ini akan menjelaskan hal ini, saya pikir. Pertanyaannya adalah tentang apa yang membuat dua pendekatan berbeda optimal untuk kasus penggunaan yang sesuai.
Karl Knechtel
Perhatikan bahwa PyPy menggunakan urutan yang sama untuk keduanya dictdan setsejak 2.7.
MisterMiyagi

Jawaban:

10

Perangkat dan dikte dioptimalkan untuk berbagai kasus penggunaan. Penggunaan utama dari set adalah pengujian keanggotaan cepat, yang merupakan pesanan agnostik.Untuk dicts, biaya pencarian adalah operasi yang paling kritis, dan kuncinya lebih mungkin ada. Dengan set, ada atau tidaknya elemen tidak diketahui sebelumnya, dan implementasi set perlu dioptimalkan untuk kasus yang ditemukan dan tidak ditemukan. Juga, beberapa optimisasi untuk operasi himpunan umum seperti union dan persimpangan membuat sulit untuk mempertahankan pemesanan himpunan tanpa menurunkan kinerja.

Sementara kedua struktur data berdasarkan hash, itu adalah kesalahpahaman umum bahwa set hanya diimplementasikan sebagai dikts dengan nilai nol. Bahkan sebelum implementasi dict kompak dalam CPython 3.6, implementasi set dan dict sudah berbeda secara signifikan, dengan sedikit kode yang digunakan kembali. Misalnya, dicts menggunakan probing acak, tetapi set menggunakan kombinasi linear probing dan open addressing, untuk meningkatkan lokalitas cache. Probe linear awal (default 9 langkah dalam CPython) akan memeriksa serangkaian pasangan kunci / hash yang berdekatan, meningkatkan kinerja dengan mengurangi biaya penanganan benturan hash - akses memori berturut-turut lebih murah daripada probe yang tersebar.

Secara teori dimungkinkan untuk mengubah implementasi himpunan CPython menjadi serupa dengan dikt kompak, tetapi dalam praktiknya ada kelemahan, dan pengembang inti terkenal menentang untuk melakukan perubahan semacam itu.

Set tetap tidak teratur. (Mengapa? Pola penggunaannya berbeda. Juga, implementasinya berbeda.)

- Guido van Rossum

Set menggunakan algoritma yang berbeda yang tidak dapat diubah untuk mempertahankan urutan penyisipan. Operasi set-ke-set kehilangan fleksibilitas dan optimalisasi jika diperlukan. Himpunan matematika didefinisikan dalam hal himpunan tidak teratur. Singkatnya, mengatur pemesanan tidak dalam waktu dekat.

- Raymond Hettinger

Diskusi terperinci tentang apakah akan memadatkan set untuk 3.7, dan jawaban tentang mengapa diputuskan menentang, dapat ditemukan di milis python-dev.

Singkatnya, poin utama adalah bahwa pola penggunaannya berbeda (perintah pemesanan penyisipan seperti ** kwarg berguna , kurang begitu untuk set), penghematan ruang untuk set compacting kurang signifikan (karena hanya ada kunci dan array hash untuk densifikasi, tidak seperti kunci, hash dan nilai), dan optimisasi probing linier dalam set tidak sesuai dengan implementasi yang kompak.

Saya akan mereproduksi posting Raymond di bawah ini yang mencakup poin paling penting.

Pada 14 Sep 2016, pada jam 15:50, Eric Snow menulis:

Lalu, saya akan melakukan hal yang sama untuk set.

Kecuali saya salah paham, Raymond menentang untuk melakukan perubahan yang sama dengan pengaturan.

Betul. Berikut adalah beberapa pemikiran tentang masalah ini sebelum orang mulai berlari liar.

  • Untuk dikt kompak, penghematan ruang adalah kemenangan bersih dengan ruang tambahan yang dikonsumsi oleh indeks dan alokasi keseluruhan untuk array kunci / nilai / hash lebih dari diimbangi oleh peningkatan kepadatan array kunci / nilai / hash. Namun untuk set, net jauh lebih tidak menguntungkan karena kita masih membutuhkan indeks dan alokasi keseluruhan tetapi hanya dapat mengimbangi biaya ruang dengan memadatkan hanya dua dari tiga array. Dengan kata lain, pemadatan lebih masuk akal ketika Anda telah menyia-nyiakan ruang untuk kunci, nilai, dan hash. Jika Anda kehilangan salah satu dari ketiganya, itu berhenti memaksa.

  • Pola penggunaan untuk set berbeda dengan dikt. Yang pertama memiliki lebih banyak hit atau miss lookup. Yang terakhir cenderung memiliki lebih sedikit pencarian kunci yang hilang. Selain itu, beberapa optimisasi untuk operasi set-to-set membuatnya sulit untuk mempertahankan urutan pemesanan tanpa mempengaruhi kinerja.

  • Saya mengejar jalur alternatif untuk meningkatkan kinerja yang ditetapkan. Alih-alih memadatkan (yang tidak banyak ruang menang dan mengeluarkan biaya tipuan tambahan), saya menambahkan linear probing untuk mengurangi biaya tabrakan dan meningkatkan kinerja cache. Peningkatan ini tidak sesuai dengan pendekatan pemadatan yang saya anjurkan untuk kamus.

  • Untuk saat ini, efek samping pemesanan pada kamus tidak dijamin, jadi terlalu dini untuk mulai bersikeras bahwa set juga dipesan. Dokumen sudah ditautkan ke resep untuk membuat OrderedSet ( https://code.activestate.com/recipes/576694/ ) tetapi sepertinya serapannya hampir nol. Juga, sekarang Eric Snow telah memberi kami OrderedDict cepat, lebih mudah dari sebelumnya untuk membuat OrderedSet dari MutableSet dan OrderedDict, tapi sekali lagi saya belum melihat minat nyata karena analisis data set-to-set yang khas tidak benar-benar perlu atau peduli tentang pemesanan. Demikian juga, penggunaan utama pengujian keanggotaan cepat adalah pesanan agnostik.

  • Yang mengatakan, saya pikir ada ruang untuk menambahkan implementasi set alternatif ke PyPI. Secara khusus, ada beberapa kasus khusus yang menarik untuk data yang dapat dipesan di mana operasi set-to-set dapat dipercepat dengan membandingkan seluruh rentang kunci (lihat https://code.activestate.com/recipes/230113-implementation-of- set-using-sort-list untuk titik awal). IIRC, PyPI sudah memiliki kode untuk filter bloom set-seperti dan hashing cuckoo.

  • Saya memahami bahwa menarik untuk memiliki blok kode utama yang diterima ke dalam inti Python tetapi itu seharusnya tidak terbuka bagi pintu air untuk terlibat dalam penulisan ulang yang lebih utama dari tipe data lain kecuali kami yakin itu dijamin.

- Raymond Hettinger

Dari [Python-Dev] Python 3.6 dict menjadi ringkas dan mendapatkan versi pribadi; dan kata kunci dipesan , September 2016.

wim
sumber
2

Diskusi

Pertanyaan Anda erat dan sudah banyak dibahas di python-devs belum lama ini. R. Hettinger membagikan daftar alasan di utas itu . Keadaan masalah sekarang tampak terbuka sekarang, tak lama setelah jawaban rinci dari T. Peters ini.

Singkatnya, penerapan dikte modern yang mempertahankan urutan penyisipan adalah unik dan tidak dianggap sesuai dengan set. Secara khusus, dikt digunakan di mana-mana untuk menjalankan Python (misalnya __dict__dalam ruang nama objek). Motivasi utama di balik dikte modern adalah untuk mengurangi ukuran, membuat Python lebih efisien secara keseluruhan. Sebaliknya, set kurang umum daripada dikte dalam inti Python dan dengan demikian menghalangi refactoring tersebut. Lihat juga pembicaraan R. Hettinger tentang implementasi dikt modern.


Perspektif

Sifat unordered set dalam Python sejajar dengan perilaku set matematika . Pesanan tidak dijamin.

Konsep matematika yang sesuai tidak berurutan dan akan aneh untuk memaksakan seperti urutan - R. Hettinger

Jika urutan apa pun diperkenalkan ke set dengan Python, maka perilaku ini akan sesuai dengan struktur matematika yang benar-benar terpisah, yaitu set yang dipesan (atau Oset). Osets memainkan peran yang terpisah dalam matematika, khususnya dalam kombinatorik. Salah satu aplikasi praktis Osets diamati dalam perubahan bel .

Memiliki set unordered konsisten dengan struktur data yang sangat generik dan di mana-mana yang membuka pin kebanyakan matematika modern, yaitu Set Theory . Saya serahkan, set unordered dengan Python baik untuk dimiliki.

Lihat juga posting terkait yang memperluas topik ini:

pylang
sumber