Bagaimana cara menghindari kesalahan "RuntimeError: kamus berubah ukuran saat iterasi"?

258

Saya telah memeriksa semua pertanyaan lain dengan kesalahan yang sama namun tidak menemukan solusi yang bermanfaat = /

Saya punya kamus daftar:

d = {'a': [1], 'b': [1, 2], 'c': [], 'd':[]}

di mana beberapa nilai kosong. Di akhir pembuatan daftar ini, saya ingin menghapus daftar kosong ini sebelum mengembalikan kamus saya. Saat ini saya mencoba melakukan ini sebagai berikut:

for i in d:
    if not d[i]:
        d.pop(i)

Namun, ini memberi saya kesalahan runtime. Saya menyadari bahwa Anda tidak dapat menambah / menghapus elemen dalam kamus saat iterasi melalui itu ... apa yang akan menjadi cara di sekitar ini?

pengguna1530318
sumber

Jawaban:

455

Di Python 2.x, panggilan keysmembuat salinan kunci yang dapat Anda ulangi saat memodifikasi dict:

for i in d.keys():

Perhatikan bahwa ini tidak berfungsi di Python 3.x karena keysmengembalikan iterator, bukan daftar.

Cara lain adalah menggunakan listuntuk memaksa salinan kunci yang akan dibuat. Yang ini juga berfungsi di Python 3.x:

for i in list(d):
Mark Byers
sumber
1
Saya yakin maksud Anda 'menelepon keysmembuat salinan kunci yang dapat Anda lakukan berulang kali' alias pluralkunci - kunci itu, kan? Kalau tidak, bagaimana orang bisa mengulanginya dengan satu kunci? Ngomong-ngomong, saya tidak memetiknya, saya benar-benar tertarik untuk mengetahui apakah itu memang kunci atau kunci
HighOnMeat
6
Atau tuple bukan daftar karena lebih cepat.
Brambor
8
Untuk memperjelas perilaku python 3.x, d.keys () mengembalikan sebuah iterable (bukan iterator) yang berarti bahwa itu adalah pandangan pada kunci kamus secara langsung. Penggunaan for i in d.keys()sebenarnya bekerja di python 3.x secara umum , tetapi karena itu adalah iterating atas pandangan iterable dari kunci kamus, memanggil d.pop()selama loop mengarah ke kesalahan yang sama seperti yang Anda temukan. for i in list(d)meniru perilaku python 2 yang sedikit tidak efisien menyalin kunci ke daftar sebelum iterasi, untuk keadaan khusus seperti milik Anda.
Michael Krebs
solusi python3 Anda tidak berfungsi ketika Anda ingin menghapus objek di dalam dikt. misalnya Anda memiliki dic A dan dict B di dict A. jika Anda ingin menghapus objek di dict B itu terjadi kesalahan
Ali-T
1
Dalam python3.x, list(d.keys())menciptakan output yang sama seperti list(d), menyerukan listpada dictpengembalian tombol. The keyscall (meskipun tidak mahal) tidak perlu.
Sean Breckenridge
46

Cukup gunakan pemahaman kamus untuk menyalin item yang relevan ke dikt baru

>>> d
{'a': [1], 'c': [], 'b': [1, 2], 'd': []}
>>> d = { k : v for k,v in d.iteritems() if v}
>>> d
{'a': [1], 'b': [1, 2]}

Untuk ini dalam Python 3

>>> d
{'a': [1], 'c': [], 'b': [1, 2], 'd': []}
>>> d = { k : v for k,v in d.items() if v}
>>> d
{'a': [1], 'b': [1, 2]}
Maria Zverina
sumber
11
d.iteritems()memberi saya kesalahan. d.items()Sebagai gantinya saya menggunakan - menggunakan python3
wcyn
4
Ini berfungsi untuk masalah yang diajukan dalam pertanyaan OP. Namun siapa pun yang datang ke sini setelah mengenai RuntimeError ini dalam kode multi-utas, ketahuilah bahwa GIL CPython dapat dirilis di tengah-tengah pemahaman daftar juga dan Anda harus memperbaikinya secara berbeda.
Yirkha
40

Anda hanya perlu menggunakan "salin":

Dengan cara itu Anda mengulangi bidang kamus asli dan dengan cepat dapat mengubah dict yang diinginkan (d dict). Ini berfungsi pada setiap versi python, jadi lebih jelas.

In [1]: d = {'a': [1], 'b': [1, 2], 'c': [], 'd':[]}

In [2]: for i in d.copy():
   ...:     if not d[i]:
   ...:         d.pop(i)
   ...:         

In [3]: d
Out[3]: {'a': [1], 'b': [1, 2]}
Alon Elharar
sumber
Bekerja! Terima kasih.
Guilherme Carvalho Lithg
13

Saya akan mencoba menghindari memasukkan daftar kosong di tempat pertama, tetapi, umumnya akan menggunakan:

d = {k: v for k,v in d.iteritems() if v} # re-bind to non-empty

Jika sebelum 2.7:

d = dict( (k, v) for k,v in d.iteritems() if v )

atau hanya:

empty_key_vals = list(k for k in k,v in d.iteritems() if v)
for k in empty_key_vals:
    del[k]
Jon Clements
sumber
+1: opsi terakhir menarik karena hanya menyalin kunci dari item-item yang perlu dihapus. Ini dapat memberikan kinerja yang lebih baik jika hanya sejumlah kecil item yang perlu dihapus relatif terhadap ukuran dikt.
Mark Byers
@MarkByers yup - dan jika banyak, maka mengikat kembali dikt tersebut ke yang baru yang difilter adalah pilihan yang lebih baik. Itu selalu harapan tentang bagaimana struktur harus bekerja
Jon Clements
4
Salah satu bahaya dengan memberontak adalah jika di suatu tempat dalam program ada objek yang memegang referensi ke dikt lama tidak akan melihat perubahan. Jika Anda yakin bukan itu masalahnya, maka tentu saja ... itu pendekatan yang masuk akal, tetapi penting untuk dipahami bahwa itu tidak sama dengan memodifikasi dict asli.
Mark Byers
@MarkByers poin yang sangat bagus - Anda dan saya tahu itu (dan orang lain yang tak terhitung jumlahnya), tapi itu tidak jelas bagi semua. Dan saya akan menaruh uang di atas meja itu juga tidak menggigit Anda di bagian belakang :)
Jon Clements
Titik menghindari memasukkan entri kosong adalah yang sangat bagus.
Magnus Bodin
12

Untuk Python 3:

{k:v for k,v in d.items() if v}
ucyo
sumber
Bagus dan ringkas. Bekerja untuk saya di Python 2.7 juga.
donrondadon
6

Anda tidak dapat mengulangi melalui kamus saat itu berubah selama untuk loop. Buat casting untuk daftar dan beralih ke daftar itu, itu bekerja untuk saya.

    for key in list(d):
        if not d[key]: 
            d.pop(key)
Alvaro Romero Diaz
sumber
1

Untuk situasi seperti ini, saya ingin membuat salinan dalam dan loop melalui salinan itu sambil memodifikasi dict asli.

Jika bidang pencarian ada di dalam daftar, Anda dapat menghitung dalam loop for the list dan kemudian menentukan posisi sebagai indeks untuk mengakses bidang dalam dict asli.

Ajayi Oluwaseun Emmanuel
sumber
1

Ini bekerja untuk saya:

dict = {1: 'a', 2: '', 3: 'b', 4: '', 5: '', 6: 'c'}
for key, value in list(dict.items()):
    if (value == ''):
        del dict[key]
print(dict)
# dict = {1: 'a', 3: 'b', 6: 'c'}  

Casting item kamus ke daftar membuat daftar itemnya, sehingga Anda dapat mengulanginya dan menghindari RuntimeError.

singrium
sumber
1

dictc = {"stName": "asas"} keys = dictc.keys () untuk kunci dalam daftar (kunci): dictc [key.upper ()] = Cetak 'nilai baru' (str (dictc))

vaibhav.patil
sumber
0

Alasan untuk kesalahan runtime adalah bahwa Anda tidak dapat mengulangi melalui struktur data saat strukturnya berubah selama iterasi.

Salah satu cara untuk mencapai apa yang Anda cari adalah dengan menggunakan daftar untuk menambahkan kunci yang ingin Anda hapus dan kemudian menggunakan fungsi pop pada kamus untuk menghapus kunci yang diidentifikasi saat iterasi melalui daftar.

d = {'a': [1], 'b': [1, 2], 'c': [], 'd':[]}
pop_list = []

for i in d:
        if not d[i]:
                pop_list.append(i)

for x in pop_list:
        d.pop(x)
print (d)
Rohit
sumber
0

Python 3 tidak mengizinkan penghapusan saat iterasi (menggunakan untuk loop di atas) kamus. Ada berbagai alternatif untuk dilakukan; salah satu cara sederhana adalah mengubah baris berikut

for i in x.keys():

Dengan

for i in list(x)
Hasham
sumber