Saya mencari cara untuk memperbarui kamus dict1 dengan isi pembaruan dict tanpa menimpa levelA
dictionary1={'level1':{'level2':{'levelA':0,'levelB':1}}}
update={'level1':{'level2':{'levelB':10}}}
dictionary1.update(update)
print dictionary1
{'level1': {'level2': {'levelB': 10}}}
Saya tahu pembaruan itu menghapus nilai-nilai di level2 karena memperbarui kunci terendah level1.
Bagaimana saya bisa mengatasi ini, mengingat bahwa dictionary1 dan pembaruan dapat memiliki panjang?
Jawaban:
Jawaban @ FM memiliki ide umum yang tepat, yaitu solusi rekursif, tetapi agak aneh pengkodean dan setidaknya satu bug. Saya akan merekomendasikan, sebagai gantinya:
Python 2:
Python 3:
Bug muncul ketika "pembaruan" memiliki
k
,v
item di manav
adict
dan padak
awalnya bukan kunci dalam kamus yang diperbarui - @ FM kode "melompati" bagian ini dari pembaruan (karena melakukan itu pada yang baru kosongdict
yang tidak disimpan atau dikembalikan ke mana pun, hilang begitu saja ketika panggilan rekursif kembali).Perubahan saya yang lain kecil: tidak ada alasan untuk
if
/else
membangun kapan.get
melakukan pekerjaan yang sama lebih cepat dan lebih bersih, danisinstance
paling baik diterapkan pada kelas dasar abstrak (bukan yang konkret) untuk umum.sumber
isinstance
tes, tetapi saya pikir saya akan mencobanya.TypeError: 'int' object does not support item assignment.
ketika Anda, misupdate({'k1': 1}, {'k1': {'k2': 2}})
. Untuk mengubah perilaku ini, dan alih-alih memperluas kedalaman kamus untuk memberikan ruang bagi kamus yang lebih dalam, Anda dapat menambahkanelif isinstance(d, Mapping):
sekitard[k] = u[k]
dan setelahisinstance
kondisi. Anda juga harus menambahkan sebuahelse: d = {k: u[k]}
untuk menangani kasus bahwa pemutakhiran dikt lebih dalam dari dikt asli. Senang mengedit jawabannya, tetapi tidak ingin mengotori kode ringkas yang memecahkan masalah OP.isinstance(v, collections.Mapping)
bukanisinstance(v, dict)
? Jika OP memutuskan untuk mulai menggunakan koleksi?u.iteritems()
keu.items()
, jika tidak, Anda akan menemukan:AttributeError: 'dict' object has no attribute 'iteritems'
Saya perlu sedikit tentang hal ini, tetapi berkat pos @ Alex, dia mengisi celah yang saya lewatkan. Namun, saya menemukan masalah jika nilai dalam rekursif
dict
terjadi menjadilist
, jadi saya pikir saya akan berbagi, dan memperluas jawabannya.sumber
orig_dict.get(key, []) + val
.merged_tree = update({'default': {'initialvalue': 1}}, other_tree)
@ Jawaban Alex bagus, tetapi tidak berfungsi saat mengganti elemen seperti integer dengan kamus, seperti
update({'foo':0},{'foo':{'bar':1}})
. Pembaruan ini mengatasinya:sumber
elif
cek saya pada objek asli tipe "bersyarat" bersyarat yang berisi cek nilai dan kunci dari dikt / pemetaan itu. Pintar.update({'A1': 1, 'A2':2}, {'A1': {'B1': {'C1': 3, 'C2':4}, 'B2':2}, 'A3':5})
. Apakah Anda memiliki contoh yang tidak melakukan apa yang Anda inginkan?if isinstance(d, collections.Mapping)
pada iterasi evey? Lihat jawaban saya .Solusi yang sama dengan yang diterima, tetapi penamaan variabel yang lebih jelas, docstring, dan memperbaiki bug di mana
{}
sebagai nilai tidak akan menimpa.Berikut adalah beberapa kasus uji:
Fungsi ini tersedia dalam paket penipu , di
charlatan.utils
.sumber
Berikut adalah versi kamus gabungan rekursif yang tidak berubah jika ada yang membutuhkannya.
Berdasarkan jawaban @Alex Martelli .
Python 2.x:
Python 3.x:
sumber
Perbaikan kecil pada jawaban @ Alex yang memungkinkan pembaruan kamus dengan kedalaman yang berbeda serta membatasi kedalaman yang dibelokkan ke dalam kamus bersarang yang asli (tetapi kedalaman kamus yang diperbarui tidak terbatas). Hanya beberapa kasus yang telah diuji:
sumber
update({'k1': 1}, {'k1': {'k2': {'k3': 3}}})
Saya menambahkan jawaban yang membahas iniif isinstance(d, Mapping)
pada iterasi evey? Lihat jawaban saya . (Juga, saya tidak yakin tentang Andad = {k: u[k]}
)Pertanyaan ini sudah lama, tetapi saya mendarat di sini ketika mencari solusi "penggabungan yang mendalam". Jawaban di atas mengilhami apa yang berikut. Saya akhirnya menulis sendiri karena ada bug di semua versi yang saya uji. Titik kritis yang terlewatkan adalah, pada kedalaman sewenang-wenang dari dua dicts input, untuk beberapa kunci, k, pohon keputusan ketika d [k] atau u [k] bukan dict yang salah.
Juga, solusi ini tidak memerlukan rekursi, yang lebih simetris dengan cara
dict.update()
kerjanya, dan kembaliNone
.sumber
Cukup gunakan
python-benedict
(saya lakukan) , ia memilikimerge
metode utilitas (deepupdate) dan banyak lainnya. Ia bekerja dengan python 2 / python 3 dan sudah teruji dengan baik.Instalasi:
pip install python-benedict
Dokumentasi: https://github.com/fabiocaccamo/python-benedict
sumber
Dalam kedua jawaban ini penulis tampaknya memahami konsep memperbarui objek yang disimpan dalam kamus atau bahkan iterasi item kamus (sebagai lawan dari kunci). Jadi saya harus menulis yang tidak membuat toko kamus tautologis dan pengambilan. Dicts diasumsikan menyimpan dicts lain atau tipe sederhana.
Atau bahkan yang lebih sederhana bekerja dengan jenis apa pun:
sumber
Perbarui ke jawaban @Alex Martelli untuk memperbaiki bug dalam kode untuk membuat solusi lebih kuat:
Kuncinya adalah kita sering ingin membuat tipe yang sama saat rekursi, jadi di sini kita gunakan
v.copy().clear()
tetapi tidak{}
. Dan ini sangat berguna jika didict
sini adalah tipecollections.defaultdict
yang dapat memiliki berbagai jenisdefault_factory
.Perhatikan juga bahwa
u.iteritems()
telah diubah keu.items()
dalamPython3
.sumber
Saya menggunakan solusi yang disarankan @Alex Martelli, tetapi gagal
TypeError 'bool' object does not support item assignment
ketika dua kamus berbeda dalam tipe data pada tingkat tertentu.
Dalam kasus pada tingkat yang sama, elemen kamus
d
hanyalah skalar (mis.Bool
) Sedangkan elemen kamusu
masih berupa kamus, pergantian tugas gagal karena tidak ada tugas kamus yang dimungkinkan menjadi skalar (sepertiTrue[k]
).Satu kondisi tambahan memperbaiki bahwa:
sumber
Kode di bawah ini harus menyelesaikan
update({'k1': 1}, {'k1': {'k2': 2}})
masalah dalam jawaban @Alex Martelli dengan cara yang benar.sumber
gunakan
dict
ataucollections.Mapping
sumber
Saya tahu pertanyaan ini cukup lama, tetapi masih memposting apa yang saya lakukan ketika saya harus memperbarui kamus bersarang. Kita dapat menggunakan fakta bahwa dicts dilewatkan dengan referensi dalam python Dengan asumsi bahwa jalur kunci diketahui dan dipisahkan oleh titik. Valas jika kita memiliki data bernama dict:
Dan kami ingin memperbarui kelas antrian, jalur kuncinya adalah -
log_config_worker.handlers.queue.class
Kita dapat menggunakan fungsi berikut untuk memperbarui nilai:
Ini akan memperbarui kamus dengan benar.
sumber
Bisa jadi Anda menemukan kamus yang tidak standar, seperti saya hari ini, yang tidak memiliki atribut-iteritem. Dalam hal ini mudah untuk menafsirkan jenis kamus ini sebagai kamus standar. Misalnya: Python 2.7:
Python 3.8:
sumber
Iya! Dan solusi lain. Solusi saya berbeda pada tombol yang sedang diperiksa. Dalam semua solusi lain, kami hanya melihat kunci di
dict_b
. Namun di sini kita melihat penyatuan kedua kamus.Lakukan dengan itu sesukamu
sumber
Jika Anda ingin mengganti "kamus bersarang penuh dengan array", Anda dapat menggunakan potongan ini:
Ini akan mengganti "old_value" dengan "new_value". Secara kasar melakukan pembangunan kembali kamus yang mendalam. Ia bahkan dapat bekerja dengan List atau Str / int yang diberikan sebagai parameter input level pertama.
sumber
Cara lain menggunakan rekursi:
sumber
sebuah Q baru Bagaimana dengan rantai kunci
sumber
Anda dapat mencoba ini, ini berfungsi dengan daftar dan murni:
sumber
Saya merekomendasikan untuk mengganti
{}
dengantype(v)()
untuk menyebarkan jenis objek dari setiap subclass dict disimpanu
tetapi tidak adad
. Misalnya, ini akan melestarikan jenis seperti koleksi.OrderedDict:Python 2:
Python 3:
sumber
Itu agak ke samping tetapi apakah Anda benar-benar membutuhkan kamus bersarang? Bergantung pada masalahnya, terkadang kamus datar mungkin cukup ... dan terlihat bagus:
sumber
Jika Anda menginginkan one-liner:
sumber