Apakah sah untuk menghapus item dari kamus dengan Python saat iterasi?
Sebagai contoh:
for k, v in mydict.iteritems():
if k == val:
del mydict[k]
Idenya adalah untuk menghapus elemen yang tidak memenuhi kondisi tertentu dari kamus, alih-alih membuat kamus baru yang merupakan bagian dari yang sedang diulangi.
Apakah ini solusi yang baik? Apakah ada cara yang lebih elegan / efisien?
scripting
dictionary
python
Trilarion
sumber
sumber
Jawaban:
EDIT:
Jawaban ini tidak akan berfungsi untuk Python3 dan akan memberikan
RuntimeError
.Ini terjadi karena
mydict.keys()
mengembalikan iterator bukan daftar. Seperti yang ditunjukkan dalam komentar cukup mengkonversimydict.keys()
ke daftar olehlist(mydict.keys())
dan itu harus berfungsi.Tes sederhana di konsol menunjukkan Anda tidak dapat memodifikasi kamus saat iterating di atasnya:
Seperti yang dinyatakan dalam jawaban delnan, menghapus entri menyebabkan masalah ketika iterator mencoba untuk pindah ke entri berikutnya. Sebagai gantinya, gunakan
keys()
metode untuk mendapatkan daftar kunci dan bekerja dengan itu:Jika Anda perlu menghapus berdasarkan nilai item, gunakan
items()
metode ini sebagai gantinya:sumber
for k, v in list(mydict.items()):
yang berfungsi dengan baik di Python 3. Sama untukkeys()
menjadilist(keys())
.RuntimeError: dictionary changed size during iteration
for k in list(mydict.keys()):
sebagai python3 membuat kunci () metode iterator, dan juga melarang menghapus item dict selama iterasi. Dengan menambahkan panggilan daftar () Anda mengubah kunci () iterator menjadi daftar. Jadi, ketika Anda berada di badan for loop Anda tidak lagi beralih ke kamus itu sendiri.Anda juga bisa melakukannya dalam dua langkah:
Pendekatan favorit saya biasanya hanya membuat dict baru:
sumber
remove
pendekatan loop.for k in [k for k in mydict if k == val]: del mydict[k]
Anda tidak dapat mengubah koleksi saat mengulanginya. Dengan cara itu ada kegilaan - terutama, jika Anda diizinkan untuk menghapus dan menghapus item saat ini, iterator harus pindah (+1) dan panggilan berikutnya
next
akan membawa Anda melampaui itu (+2), jadi Anda akan akhirnya melewatkan satu elemen (yang tepat di belakang yang Anda hapus). Anda memiliki dua opsi:.keys()
et al untuk ini (dalam Python 3, meneruskan iterator yang dihasilkan kelist
). Bisa jadi sangat boros ruang-bijaksana.mydict
seperti biasa, menyimpan kunci untuk dihapus dalam koleksi terpisahto_delete
. Setelah selesai iterasimydict
, hapus semua itemto_delete
darimydict
. Menyimpan beberapa (tergantung pada berapa banyak tombol yang dihapus dan berapa banyak yang tersisa) ruang selama pendekatan pertama, tetapi juga membutuhkan beberapa baris lagi.sumber
You can't modify a collection while iterating it.
ini hanya benar untuk dikte dan teman, tetapi Anda dapat mengubah daftar selama iterasi:L = [1,2,None,4,5] <\n> for n,x in enumerate(L): <\n\t> if x is None: del L[n]
can't
hanya benar untuk dict dan teman-teman, sementara itu harusshouldn't
untuk daftar.Iterate alih-alih salinan, seperti yang dikembalikan oleh
items()
:sumber
del v
secara langsung, jadi Anda telah membuat salinan dari masing-masing v yang tidak akan pernah Anda gunakan dan Anda harus mengakses item dengan kunci.dict.keys()
adalah pilihan yang lebih baik.v
sebagai kriteria untuk dihapus.dict.items()
mengembalikan iterator daripada salinan. Lihat komentar untuk Blair 's jawaban , yang (sayangnya) juga mengasumsikan Python 2 semantik.Ini paling bersih untuk digunakan
list(mydict)
:Ini sesuai dengan struktur paralel untuk daftar:
Keduanya bekerja di python2 dan python3.
sumber
Anda dapat menggunakan pemahaman kamus.
d = {k:d[k] for k in d if d[k] != val}
sumber
d
di tempat.Dengan python3, iterate on dic.keys () akan meningkatkan kesalahan ukuran kamus. Anda dapat menggunakan cara alternatif ini:
Diuji dengan python3, itu berfungsi dengan baik dan Kesalahan " kamus berubah ukuran selama iterasi " tidak dinaikkan:
sumber
Pertama-tama Anda bisa membuat daftar kunci untuk dihapus, dan kemudian beralih dari daftar itu menghapusnya.
sumber
Ada cara yang mungkin cocok jika item yang ingin Anda hapus selalu di "awal" dari iterasi dikte
"Permulaan" hanya dijamin konsisten untuk versi / implementasi Python tertentu. Misalnya dari What's New In Python 3.7
Cara ini menghindari salinan dikt yang disarankan oleh banyak jawaban lain, setidaknya dalam Python 3.
sumber
Saya mencoba solusi di atas dalam Python3 tetapi yang satu ini tampaknya menjadi satu-satunya yang berfungsi untuk saya ketika menyimpan objek dalam dict. Pada dasarnya Anda membuat salinan dikt Anda () dan beralihi itu sambil menghapus entri dalam kamus asli Anda.
sumber