Apa objek tampilan kamus?

158

Dalam python 2.7, kami mendapatkan metode tampilan kamus yang tersedia.

Sekarang, saya tahu pro dan kontra dari yang berikut:

  • dict.items()(dan values, keys): mengembalikan daftar, sehingga Anda benar-benar dapat menyimpan hasilnya, dan
  • dict.iteritems() (dan sejenisnya): mengembalikan generator, sehingga Anda dapat mengulangi setiap nilai yang dihasilkan satu per satu.

Untuk apa dict.viewitems()(dan sejenisnya)? Apa manfaatnya? Bagaimana cara kerjanya? Apa itu pandangan?

Saya membaca bahwa tampilan selalu mencerminkan perubahan dari kamus. Tetapi bagaimana perilakunya dari sudut pandang perf dan memory? Apa pro dan kontra?

e-satis
sumber

Jawaban:

157

Tampilan kamus pada dasarnya adalah apa namanya: tampilan hanya seperti jendela pada kunci dan nilai (atau item) dari kamus. Berikut adalah kutipan dari dokumentasi resmi untuk Python 3:

>>> dishes = {'eggs': 2, 'sausage': 1, 'bacon': 1, 'spam': 500}
>>> keys = dishes.keys()
>>> values = dishes.values()

>>> # view objects are dynamic and reflect dict changes
>>> del dishes['eggs']
>>> keys  # No eggs anymore!
dict_keys(['sausage', 'bacon', 'spam'])

>>> values  # No eggs value (2) anymore!
dict_values([1, 1, 500])

(Penggunaan setara Python 2 dishes.viewkeys()dan dishes.viewvalues().)

Contoh ini menunjukkan karakter dinamis dari tampilan : tampilan kunci bukan salinan kunci pada titik waktu tertentu, tetapi lebih merupakan jendela sederhana yang menunjukkan kepada Anda kunci; jika mereka diubah, maka apa yang Anda lihat melalui jendela juga berubah. Fitur ini dapat berguna dalam beberapa keadaan (misalnya, seseorang dapat bekerja dengan tampilan pada tombol di beberapa bagian program alih-alih menghitung ulang daftar kunci saat ini setiap kali diperlukan) —Catat bahwa jika kunci kamus dimodifikasi sementara iterating over the view, bagaimana iterator seharusnya berperilaku tidak didefinisikan dengan baik, yang dapat menyebabkan kesalahan .

Satu keuntungan adalah bahwa melihat , katakanlah, tombol hanya menggunakan sejumlah kecil dan memori tetap dan membutuhkan sejumlah kecil dan waktu prosesor tetap , karena tidak ada pembuatan daftar kunci (Python 2, di sisi lain, sering kali tidak perlu membuat daftar baru, seperti dikutip oleh Rajendran T, yang mengambil memori dan waktu dalam jumlah yang sebanding dengan panjang daftar). Untuk melanjutkan analogi jendela, jika Anda ingin melihat lanskap di balik dinding, Anda cukup membuat celah di dalamnya (Anda membangun sebuah jendela); menyalin kunci ke dalam daftar akan sama dengan mengecat salinan lanskap di dinding Anda — salinan membutuhkan waktu, ruang, dan tidak memperbarui sendiri.

Untuk meringkas, tampilan hanyalah ... tampilan (windows) pada kamus Anda, yang menunjukkan isi kamus bahkan setelah itu berubah. Mereka menawarkan fitur yang berbeda dari daftar: daftar kunci berisi salinan kunci kamus pada titik waktu tertentu, sementara tampilan dinamis dan lebih cepat diperoleh, karena tidak harus menyalin data apa pun ( kunci atau nilai) agar dapat dibuat.

Eric O Lebigot
sumber
6
+1. Oke, bagaimana bedanya dengan memiliki akses langsung ke daftar kunci internal? Apakah itu lebih cepat, lebih lambat? Memori lebih efisien? Dibatasi? Jika Anda dapat membaca dan mengeditnya, rasanya persis sama dengan memiliki referensi ke daftar ini.
e-satis
3
Terima kasih. Masalahnya adalah bahwa pandangan adalah akses Anda ke "daftar kunci internal" (perhatikan bahwa "daftar kunci" ini bukan daftar Python, tetapi justru merupakan tampilan). Tampilan lebih hemat memori daripada daftar kunci (atau nilai atau item) dari Python 2, karena mereka tidak menyalin apa pun; mereka memang seperti "referensi ke daftar kunci" (perhatikan juga bahwa "referensi ke daftar" sebenarnya hanya disebut daftar, dengan Python, karena daftar adalah objek yang dapat diubah). Perhatikan juga bahwa Anda tidak dapat langsung mengedit tampilan: sebagai gantinya, Anda masih mengedit kamus, dan tampilan tersebut mencerminkan perubahan Anda dengan segera.
Eric O Lebigot
3
Ok, saya belum jelas tentang implementasinya, tapi ini jawaban terbaik sejauh ini.
e-satis
2
Terima kasih. Memang, jawaban ini sebagian besar tentang semantik pandangan. Saya tidak memiliki informasi tentang implementasi mereka di CPython, tetapi saya akan menebak bahwa suatu tampilan pada dasarnya adalah pointer ke struktur yang tepat (kunci dan / atau nilai-nilai), dan bahwa struktur adalah bagian dari objek kamus itu sendiri.
Eric O Lebigot
5
Saya pikir ada baiknya menunjukkan bahwa kode contoh dalam posting ini adalah dari python3 dan bukan apa yang saya dapatkan di python2.7.
snth
21

Seperti yang Anda sebutkan dict.items()mengembalikan salinan daftar kamus (kunci, nilai) pasangan yang boros dan dict.iteritems()mengembalikan iterator atas pasangan (kunci, nilai) kamus.

Sekarang ambil contoh berikut untuk melihat perbedaan antara interator dict dan tampilan dict

>>> d = {"x":5, "y":3}
>>> iter = d.iteritems()
>>> del d["x"]
>>> for i in iter: print i
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RuntimeError: dictionary changed size during iteration

Sedangkan tampilan hanya menunjukkan apa yang ada dalam dikt. Tidak masalah jika itu berubah:

>>> d = {"x":5, "y":3}
>>> v = d.viewitems()
>>> v
dict_items([('y', 3), ('x', 5)])
>>> del d["x"]
>>> v
dict_items([('y', 3)])

Tampilan hanyalah tampilan kamus sekarang. Setelah menghapus suatu entri .items()akan menjadi usang dan .iteritems()akan membuat kesalahan.

Martin Konecny
sumber
Contoh yang bagus, terima kasih. Padahal, harus v = d.items () bukan v - d.viewitems ()
rix
1
Pertanyaannya adalah tentang Python 2.7, jadi viewitems()sebenarnya benar ( items()benar memberikan tampilan di Python 3 ).
Eric O Lebigot
Namun, tampilan tidak dapat digunakan untuk beralih ke kamus saat memodifikasinya.
Ioannis Filippidis
18

Hanya dari membaca dokumen saya mendapatkan kesan ini:

  1. Tampilan adalah "pseudo-set-like", karena mereka tidak mendukung pengindeksan, jadi apa yang dapat Anda lakukan dengannya adalah menguji keanggotaan dan beralih pada mereka (karena kunci mudah dipecahkan dan unik, tampilan kunci dan item lebih banyak " set-like "karena tidak mengandung duplikat).
  2. Anda dapat menyimpannya dan menggunakannya beberapa kali, seperti versi daftar.
  3. Karena mereka mencerminkan kamus yang mendasarinya, setiap perubahan dalam kamus akan mengubah tampilan, dan hampir pasti akan mengubah urutan iterasi . Jadi tidak seperti versi daftar, mereka tidak "stabil".
  4. Karena mereka mencerminkan kamus yang mendasarinya, mereka hampir pasti adalah objek proxy kecil; menyalin kunci / nilai / item akan mengharuskan mereka menonton kamus asli entah bagaimana dan menyalinnya berkali-kali ketika perubahan terjadi, yang akan menjadi implementasi yang tidak masuk akal. Jadi saya berharap sedikit memori di atas kepala, tetapi akses menjadi sedikit lebih lambat daripada langsung ke kamus.

Jadi saya kira kata kunci penggunaannya adalah jika Anda menyimpan kamus dan berulang kali mengulangi kunci / item / nilai dengan modifikasi di antaranya. Anda bisa menggunakan tampilan saja dan mengubahnya for k, v in mydict.iteritems():menjadi for k, v in myview:. Tetapi jika Anda hanya mengulangi kamus sekali saja, saya pikir versi iter masih lebih disukai.

Ben
sumber
2
+1 untuk menganalisis pro dan kontra dari beberapa informasi yang kami dapatkan.
e-satis
Jika saya membuat iterator pada tampilan, itu akan tetap tidak valid setiap kali kamus berubah. Itu masalah yang sama seperti dengan iterator di kamus itu sendiri (misalnya iteritems()). Jadi apa gunanya pandangan ini? Kapan saya senang memilikinya?
Alfe
@Alfe Anda benar, itu adalah masalah dengan iterasi kamus dan pandangan tidak membantu sama sekali. Katakanlah Anda perlu meneruskan nilai kamus ke suatu fungsi. Anda bisa menggunakan .values(), tetapi itu melibatkan membuat salinan keseluruhan sebagai daftar, yang bisa mahal. Ada .itervalues()tetapi Anda tidak dapat mengkonsumsinya lebih dari sekali, sehingga tidak akan berfungsi dengan setiap fungsi. Tampilan tidak memerlukan salinan mahal, tetapi mereka masih lebih berguna sebagai nilai yang berdiri sendiri daripada iterator. Tapi mereka masih tidak dimaksudkan untuk membantu dengan iterasi dan memodifikasi pada saat yang sama (di sana Anda benar-benar menginginkan salinan)
Ben
17

Metode tampilan mengembalikan daftar (bukan salinan daftar, dibandingkan dengan .keys(), .items()dan .values()), sehingga lebih ringan, tetapi mencerminkan isi kamus saat ini.

Dari Python 3.0 - metode dict mengembalikan tampilan - mengapa?

Alasan utamanya adalah bahwa bagi banyak kasus penggunaan, mengembalikan daftar yang sepenuhnya terpisah tidak perlu dan boros. Itu akan membutuhkan menyalin seluruh konten (yang mungkin atau banyak tidak banyak).

Jika Anda hanya ingin mengulangi kunci maka membuat daftar baru tidak perlu. Dan jika Anda memang membutuhkannya sebagai daftar terpisah (sebagai salinan) maka Anda dapat dengan mudah membuat daftar itu dari tampilan.

Rajendran T
sumber
6
Metode tampilan mengembalikan objek tampilan, yang tidak sesuai dengan antarmuka daftar.
Matthew Trevor
5

Tampilan memungkinkan Anda mengakses struktur data yang mendasari, tanpa menyalinnya. Selain dinamis daripada membuat daftar, salah satu kegunaannya yang paling berguna adalah intes. Katakanlah Anda ingin memeriksa apakah suatu nilai ada dict atau tidak (baik itu kunci atau nilai).

Opsi satu adalah membuat daftar kunci menggunakan dict.keys(), ini berfungsi tetapi jelas mengkonsumsi lebih banyak memori. Jika diktnya sangat besar? Itu akan sia-sia.

Dengan viewsAnda dapat mengulangi struktur data yang sebenarnya, tanpa daftar perantara.

Mari kita gunakan contoh. Saya memiliki dict dengan 1000 kunci string dan digit acak dan kmerupakan kunci yang ingin saya cari

large_d = { .. 'NBBDC': '0RMLH', 'E01AS': 'UAZIQ', 'G0SSL': '6117Y', 'LYBZ7': 'VC8JQ' .. }

>>> len(large_d)
1000

# this is one option; It creates the keys() list every time, it's here just for the example
timeit.timeit('k in large_d.keys()', setup='from __main__ import large_d, k', number=1000000)
13.748743600954867


# now let's create the list first; only then check for containment
>>> list_keys = large_d.keys()
>>> timeit.timeit('k in list_keys', setup='from __main__ import large_d, k, list_keys', number=1000000)
8.874809793833492


# this saves us ~5 seconds. Great!
# let's try the views now
>>> timeit.timeit('k in large_d.viewkeys()', setup='from __main__ import large_d, k', number=1000000)
0.08828549011070663

# How about saving another 8.5 seconds?

Seperti yang Anda lihat, viewobjek iterasi memberikan dorongan besar untuk kinerja, mengurangi overhead memori pada saat yang sama. Anda harus menggunakannya ketika Anda perlu melakukan Setoperasi seperti.

Catatan : Saya menggunakan Python 2.7

Chen A.
sumber
Dalam python> = 3, saya yakin .keys()mengembalikan tampilan secara default. Mungkin ingin memeriksa dua kali
Yolo Voe
1
Kamu benar. Python 3+ melakukan penggunaan berat objek tampilan alih-alih daftar, itu jauh lebih efisien secara memori
Chen A.
1
Hasil pengaturan waktu ini sangat jelas, tetapi memeriksa apakah ksalah satu kunci kamus large_ddimaksudkan untuk dilakukan k in large_d, dengan Python, yang mungkin pada dasarnya secepat menggunakan tampilan (dengan kata lain, k in large_d.keys()bukan Pythonic dan harus dihindari— apa adanya k in large_d.viewkeys()).
Eric O Lebigot
Terima kasih telah memberikan contoh yang solid dan bermanfaat. k in large_dsebenarnya jauh lebih cepat daripada yang sebenarnya k in large_d.viewkeys(), sehingga mungkin harus dihindari, tetapi ini masuk akal k in large_d.viewvalues().
naught101