Python memperbarui kunci dalam dict jika tidak ada

108

Saya ingin memasukkan pasangan nilai-kunci ke dalam dict jika kunci tidak ada di dict.keys (). Pada dasarnya saya bisa melakukannya dengan:

if key not in d.keys():
    d[key] = value

Tapi apakah ada cara yang lebih baik? Atau apa solusi pythonic untuk masalah ini?

Xiaochen Cui
sumber

Jawaban:

152

Anda tidak perlu menelepon d.keys(), jadi

if key not in d:
    d[key] = value

cukup. Tidak ada metode yang lebih jelas dan lebih mudah dibaca.

Anda bisa memperbarui lagi dengan dict.get(), yang akan mengembalikan nilai yang ada jika kunci sudah ada:

d[key] = d.get(key, value)

tetapi saya sangat menyarankan untuk tidak melakukannya; ini adalah kode golf, menghambat pemeliharaan dan keterbacaan.

Martijn Pieters
sumber
Apakah saya perlu menyinkronkan ini jika saya memiliki beberapa utas menggunakan kode?
ed22
1
@ ed22 ya, karena terdiri dari beberapa instruksi bytecode, di antaranya utas dapat beralih.
Martijn Pieters
84

Penggunaan dict.setdefault():

>>> d = {1: 'one'}
>>> d.setdefault(1, '1')
'one'
>>> d    # d has not changed because the key already existed
{1: 'one'}
>>> d.setdefault(2, 'two')
'two'
>>> d
{1: 'one', 2: 'two'}
mhawke
sumber
6
dict.setdefault()sebaiknya hanya digunakan saat mencoba mengakses nilai juga.
Martijn Pieters
14
@MartijnPieters: mengapa itu penting? Nama tersebut setdefault()dengan jelas menggambarkan apa yang terjadi - bahwa ia juga mengembalikan nilai yang tidak terlalu penting, dan IMO yang sedikit unik. Bisakah Anda memberikan penjelasan singkat, atau tautan ke salah satunya, mengapa hal ini tidak diinginkan?
mhawke
6
Anda akan menggunakannya dalam ekspresi yang mengharapkan nilai untuk eksis, seperti dalam sebuah loop pengelompokan: for obj in iterable: d.setdefault(key_for_obj(obj), []).append(obj). Tidak ada yang aneh tentang nilai yang dikembalikan, itulah inti dari metode ini .
Martijn Pieters
2
Juga, ini mengaburkan apa yang Anda coba lakukan, yaitu menyetel kunci hanya jika belum ada. Keterbacaan itu penting, cukup gunakan if key not in d:tes.
Martijn Pieters
7
@MartijnPieters: Terima kasih atas penjelasannya. Setelah membaca docstring, setdefault()sepertinya memang sesuai dengan apa yang Anda katakan. Menggunakan a defaultdict(list)akan menghasilkan kode yang lebih mudah dibaca daripada contoh Anda ... jadi mungkin tidak ada gunanya kecuali seseorang harus bekerja dengan kamus standar. Secara keseluruhan, if key not in dlebih jelas.
mhawke
25

Sejak Python 3.9 Anda dapat menggunakan operator | penggabungan untuk menggabungkan dua kamus. Dikt di sebelah kanan diutamakan:

new_dict = old_dict | { key: val }

Sebagai contoh:

new_dict = { 'a': 1, 'b': 2 } | { 'b': 42 }

print(new_dict} # {'a': 1, 'b': 42}

Catatan: ini membuat kamus baru dengan nilai yang diperbarui.

Rotareti
sumber
6
Meskipun ini menarik, saya rasa ini tidak lebih mudah dibaca daripada jawaban yang diterima
Justin Furuness
2
Bagi saya, senang mengetahui ada cara untuk menggabungkan dua penis sekarang
Austin A
Ada tantangan dengan penggabungan ini. Jika kuncinya sama, penggabungan akan mengabaikan nilainya. Jika itu tujuannya, maka itu berhasil.
Joe Ferndz
1
@JoeFerndz Perintah di sebelah kanan diutamakan dan tidak akan mengabaikan nilainya. Saya memperbarui contoh untuk membuatnya lebih jelas.
Rotareti
6

Dengan berikut ini Anda dapat menyisipkan beberapa nilai dan juga memiliki nilai default tetapi Anda sedang membuat kamus baru.

d = {**{ key: value }, **default_values}

Saya telah mengujinya dengan jawaban yang paling banyak dipilih dan rata-rata ini lebih cepat seperti yang dapat dilihat pada contoh berikut,.

Tes kecepatan membandingkan metode berbasis loop dengan pemahaman dict dengan operator unpack Tes kecepatan membandingkan metode berbasis loop dengan pemahaman dict dengan metode operator unpack.

jika tidak ada salinan ( d = default_vals.copy()) yang dibuat pada kasus pertama maka jawaban yang paling banyak dipilih akan lebih cepat setelah kita mencapai urutan besarnya 10**5dan lebih besar. Jejak memori kedua metode itu sama.

CoderKid
sumber
1
Lalu mengapa menyarankannya sejak awal
jonathan
1
Solusi ini terlihat sangat valid bagi saya. Ini juga memungkinkan memperbarui beberapa nilai dalam satu deklarasi.
Rotareti
Terima kasih banyak. Tapi sepertinya harus{**default_values, **{ key: value }}
osexp2003
0

Menurut jawaban di atas metode setdefault () bekerja untuk saya.

old_attr_name = mydict.setdefault(key, attr_name)
if attr_name != old_attr_name:
    raise RuntimeError(f"Key '{key}' duplication: "
                       f"'{old_attr_name}' and '{attr_name}'.")

Padahal solusi ini tidak generik. Hanya cocok untukku dalam kasus tertentu ini. Solusi yang tepat akan memeriksa yang keypertama (seperti yang telah disarankan), tetapi dengan setdefault()kita menghindari satu pencarian tambahan di kamus, yaitu, meskipun kecil, tetapi masih merupakan peningkatan kinerja.

pengguna10815638
sumber