Mengapa kamus python tidak dapat dibalik untuk python3.7?

11

Mulai dari 3.7, kamus python standar dijamin untuk menjaga urutan penyisipan. (*)

d = {'b': 1, 'a': 2}
for k in d: 
    print(k)
# Prints always 'b' before 'a'.

Dengan kata lain, kunci dikt disimpan dalam urutan yang ketat. Pada prinsipnya, ini akan memungkinkan kunci menjadi reversibel. Namun, tidak satu pun dari yang berikut ini yang berfungsi:

# TypeError: 'dict' object is not reversible
for k in reversed(d): 
    print(k)

# TypeError: 'dict_keys' object is not reversible
for k in reversed(d.keys()): 
    print(k)

Pertanyaan: Apa alasan di balik perilaku ini? Mengapa dikte tidak dapat dibalik? Apakah ada rencana untuk mengubah perilaku ini di masa depan?

Solusi tentu saja bekerja:

for k in reversed(list(d.keys())): 
    print(k)

(*) Sebagai soal fakta, ini sudah terjadi untuk instalasi khas python 3.6, seperti yang dibahas dalam posting ini .


Pembaruan : Dimulai dengan python 3.8 dicts sebenarnya dapat dibalik. Jawaban yang diterima mengacu pada diskusi antara Guido dan pengembang inti lainnya yang mengarah pada keputusan ini. Singkatnya, mereka menekankan konsistensi bahasa terhadap upaya implementasi dan manfaat aktual bagi pengguna.

normanius
sumber

Jawaban:

5

Dari dokumen :

terbalik ( seq )

Kembalikan terbalik iterator. seq harus berupa objek yang memiliki __reversed__()metode atau mendukung protokol urutan ( __len__()metode dan __getitem__()metode dengan argumen integer mulai dari 0).

Sebuah dictobjek tidak mengimplementasikan __reversed__. Itu menerapkan kedua metode yang terakhir. Namun, __getitem__mengambil kunci sebagai argumen, bukan bilangan bulat (mulai dari 0).

Mengenai alasannya, ini sudah disarankan dan dibahas di sini .

EDIT:

Kutipan ini dari milis Python-Dev (utas "Tambahkan metode __reversed__ untuk dict", dimulai pada 25. 05. 18), saya akan mulai dengan argumen "konseptual", yang pertama adalah dari Antoine Pitrou:

Tidak ada artinya yang sudah didukung OrderedDict terbalik (). Argumennya bisa dua arah:

  1. dict mirip dengan OrderedDict saat ini, sehingga ia harus mendukung reversed () juga;

  2. Anda dapat menggunakan OrderedDict untuk memberi sinyal secara eksplisit bahwa Anda peduli tentang pemesanan; tidak perlu menambahkan apa pun ke dikt.

Pemikiran saya adalah bahwa urutan penyisipan yang dijamin untuk diktat reguler adalah merek baru, sehingga akan memakan waktu cukup lama untuk gagasan tersebut menjadi jelas dan menjadi bagian dari pemikiran sehari-hari tentang dikt. Setelah itu terjadi, kemungkinan besar use case akan muncul dan __reversed__ akan ditambahkan di beberapa titik. Implementasinya tampak langsung dan tidak banyak lompatan konseptual untuk berharap bahwa koleksi yang dipesan terbatas akan dapat dibalik.

Diikuti oleh balasan Raymond Hettinger:

Mengingat bahwa dicts sekarang melacak urutan penyisipan, tampaknya masuk akal untuk ingin mengetahui penyisipan terbaru (yaitu perulangan atas tugas-tugas yang paling baru ditambahkan dalam tugas dict). Kasing yang mungkin digunakan lainnya kemungkinan akan terkait dengan bagaimana kita menggunakan perintah ekor Unix.

Jika kasus penggunaan tersebut muncul, alangkah baiknya jika __reversed__ sudah didukung sehingga orang tidak akan tergoda untuk mengimplementasikan solusi buruk menggunakan popitem () panggilan diikuti oleh reinsersi.

Perhatian utama yang diungkapkan dalam milis adalah bahwa ini akan menambah terlalu banyak mengasapi atau mengurangi efisiensi memori (harus memiliki daftar yang ditautkan dua kali lipat dan bukan yang terhubung satu sama lain) dalam setidaknya beberapa implementasi, inilah kutipan Inada Naoki dari Python bug tracker ( edisi 33462 ):

"Miliki pesanan" tidak berarti "dapat dibalik". Misalnya, daftar tertaut tunggal dipesan, tetapi tidak dapat dibalik.

Sementara implementasi CPython dapat memberikan efisien __reverse__, menambahkan __reverse__berarti semua implementasi Python diharapkan untuk menyediakannya. Sebagai contoh, beberapa implementasi Python mungkin dapat mengimplementasikan dict dengan hashmap + daftar tertaut tunggal. Jika __reverse__ditambahkan, itu tidak mungkin lagi.

Kembali ke milis, berikut adalah dua pesan terakhir (keduanya diposting pada 08.06.2018). Pertama dari Michael Selik:

Apakah saya benar mengatakan bahwa konsensus adalah +1 untuk dimasukkan dalam v3.8?

Poin terakhir di utas adalah INADA Naoki meneliti berbagai implementasi dan memutuskan bahwa tidak masalah untuk memasukkan fitur ini di 3.8. Seperti yang saya pahami, Guido setuju dengan saran INADA untuk menunggu implementasi v3.7 dari MicroPython. Karena INADA telah berubah pikiran, saya kira itu semua menguntungkan?

Akhiri dengan pesan Guido van Rossum:

Itu kedengarannya benar bagi saya. Kami kemudian akan memiliki dua versi di mana ini terjadi:

  • 3.6 di mana pengawetan pesanan diimplementasikan dalam CPython tetapi dalam spesifikasi bahasa

  • 3,7 di mana ia juga ditambahkan ke spek bahasa

Seperti disebutkan dalam jawaban dan komentar lainnya, reversed()didukung untuk dicts dan dictviews sejak versi 3.8 (14.10.2018).

gst
sumber
5
Kutipan kedua Anda tampak seperti seleksi bias dari utas itu. Tampaknya konsensus bahwa fungsionalitas akan ditambahkan dalam 3.8. Juga sebelum python 3.7 tidak ada pemesanan pada dictobjek normal (setidaknya tidak dijamin dari bahasa) jadi reversedjuga tidak masuk akal
FlyingTeller
Saya tidak punya anjing dalam pertarungan, saya hanya mengutip tanggapan pertamanya. Tapi Anda benar, intinya diambil dengan baik - saya menghapus kutipan.
gst
1
Terima kasih. Thread diskusi dari python-dev terbuka. Faktanya, fitur tersebut telah diimplementasikan dalam python 3.8, yang dirilis hanya dua hari yang lalu (14 Oktober 2019).
normanius
Kutipan docs tidak membantu, hanya memunculkan pertanyaan berikutnya "jadi, mengapa tipe dikt tidak diterapkan __reversed__"? Tautan python-dev memiliki konten yang bermanfaat, tetapi bagian yang relevan harus direproduksi dalam jawaban secara langsung (karena tautan di luar situs tersebut cenderung membusuk).
wim
1

Perbarui untuk python 3.8

Diktik dan dictviews sekarang dapat diubah dalam urutan penyisipan terbalik menggunakan reversed ()

>>> dict = {1: "1", 2: "2", 3: "3"}
>>> reversed(dict)
<dict_reversekeyiterator object at 0x7f72ca795130>
Гончаров Дмитрий
sumber
Apakah jawaban ini menyumbangkan sesuatu yang baru yang belum dicakup oleh jawaban, komentar, atau pertanyaan lain itu sendiri?
normanius
@normanius Ini memiliki sampel kode visual lil, mungkin membantu untuk browser cepat
jamylak