Periksa apakah kunci yang diberikan sudah ada dalam kamus


2683

Saya ingin menguji apakah ada kunci dalam kamus sebelum memperbarui nilai untuk kunci tersebut. Saya menulis kode berikut:

if 'key1' in dict.keys():
  print "blah"
else:
  print "boo"

Saya pikir ini bukan cara terbaik untuk menyelesaikan tugas ini. Apakah ada cara yang lebih baik untuk menguji kunci dalam kamus?


31
Memanggil dict.keys()membuat daftar kunci, menurut dokumentasi docs.python.org/2/library/stdtypes.html#dict.keys tetapi saya akan terkejut jika pola ini tidak dioptimalkan untuk, dalam implementasi yang serius, untuk menerjemahkan untuk if 'key1' in dict:.
Evgeni Sergeev

7
Jadi saya akhirnya tahu mengapa banyak dari script Python saya sangat lambat :) :(. Itu karena saya telah menggunakan x in dict.keys()untuk memeriksa kunci. Dan itu terjadi karena cara yang biasa untuk iterate lebih kunci di Jawa for (Type k : dict.keySet()), kebiasaan ini menyebabkan for k in dict.keys()untuk merasa lebih alami daripada for k in dict(yang seharusnya masih baik dalam hal kinerja?), tetapi kemudian memeriksa kunci if k in dict.keys()juga, yang merupakan masalah ...
Evgeni Sergeev

4
@EvgeniSergeev if k in dict_:menguji keberadaan k di KEYS dict_, jadi Anda masih tidak perlu dict_.keys(). (Ini menggigit saya, karena bunyinya bagi saya seperti pengujian untuk nilai dalam dict. Tapi tidak.)
ToolmakerSteve

1
@ToolmakerSteve Itu benar, tetapi Anda tidak hanya tidak membutuhkannya, itu bukan praktik yang baik.
Evgeni Sergeev

26
Coba "
masukkan

Jawaban:


3374

inadalah cara yang dimaksudkan untuk menguji keberadaan kunci dalam a dict.

d = {"key1": 10, "key2": 23}

if "key1" in d:
    print("this will execute")

if "nonexistent key" in d:
    print("this will not")

Jika Anda menginginkan default, Anda selalu dapat menggunakan dict.get():

d = dict()

for i in range(100):
    key = i % 10
    d[key] = d.get(key, 0) + 1

dan jika Anda ingin selalu memastikan nilai default untuk kunci apa pun Anda dapat menggunakan dict.setdefault()berulang kali atau defaultdictdari collectionsmodul, seperti:

from collections import defaultdict

d = defaultdict(int)

for i in range(100):
    d[i % 10] += 1

tetapi secara umum, inkata kunci adalah cara terbaik untuk melakukannya.


74
Saya biasanya hanya menggunakan getjika saya akan mengeluarkan item dari kamus. Tidak masuk akal dalam menggunakan in dan menarik item keluar dari kamus.
Jason Baker

75
Saya sangat setuju. Tetapi jika Anda hanya perlu tahu apakah ada kunci, atau Anda perlu membedakan antara kasus di mana kunci didefinisikan dan kasus di mana Anda menggunakan default, inadalah cara terbaik untuk melakukannya.
Chris B.

5
Referensi untuk jawaban ini ada di python docs
enkash

30
dapatkan adalah tes yang buruk jika kunci tersebut setara dengan "Salah", seperti 0misalnya. Belajar ini dengan cara yang sulit: /
Sebastien

4
Saya tidak bisa setuju bahwa ini jawaban yang lengkap karena tidak menyebutkan bahwa 'coba' - 'kecuali' akan menjadi tercepat ketika jumlah kunci gagal cukup kecil. Lihat jawaban ini di bawah ini: stackoverflow.com/a/1602945/4376643
Craig Hicks

1547

Anda tidak perlu memanggil kunci:

if 'key1' in dict:
  print("blah")
else:
  print("boo")

Itu akan jauh lebih cepat karena menggunakan hashing kamus daripada melakukan pencarian linier, yang akan dilakukan tombol panggil.


7
Itu hebat. Saya mendapat kesan bahwa secara internal masih akan melintasi daftar kunci, tetapi saya melihat ini bekerja lebih seperti menguji keanggotaan dalam satu set.
Mohan Gulati

51
@Mohan Gulati: Anda mengerti bahwa kamus adalah hashtable dari kunci yang dipetakan ke nilai, kan? Algoritma hashing mengkonversi kunci ke integer dan integer digunakan untuk menemukan lokasi di tabel hash yang cocok. en.wikipedia.org/wiki/Hash_table
hughdbrown

5
@Charles Addis, dari pengalaman bekerja dengan sekitar setengah juta kunci, Anda mendapatkan setidaknya 10x peningkatan kinerja saat menulis "key dict" daripada "key in dict.keys ()". PEP dan Zen juga menyatakan bahwa Anda harus mengabaikan mereka jika mereka buruk untuk proyek Anda.
ivan_bilan

11
ivan_bilan - Saya baru saja menjalankan benchtest saya sendiri ... Setengah juta kunci, if key in d1butuh beberapa 0.17265701293945312detik. Panggilan if key in d1.keys()mengambil 0.23871088027954102- ini adalah definisi klasik dari optimasi mikro. Menghemat 0.07884883880615234detik bukan peningkatan kinerja.
Charles Addis

11
@Eli Hanya untuk Anda Saya telah membuat tes yang dapat Anda jalankan sendiri. Hasilnya mungkin mengejutkan Anda. Untuk dikte dengan ~ 50.000 kunci, tidak menelepon keys()memberi Anda keuntungan komputasi 0,01 detik. Untuk ~ 500.000 kunci, tidak menelepon keys()memberi Anda keuntungan 0,1 detik. Untuk ~ 5.000.000 kunci, tidak menelepon keys()adalah .4 detik lebih cepat, tetapi untuk 50.000.000 kunci MEMANGGIL keys()ADALAH 3 DETIK LEBIH CEPAT!
Charles Addis

268

Anda dapat menguji keberadaan kunci dalam kamus, menggunakan kata kunci dalam :

d = {'a': 1, 'b': 2}
'a' in d # <== evaluates to True
'c' in d # <== evaluates to False

Penggunaan umum untuk memeriksa keberadaan kunci dalam kamus sebelum memutasikannya adalah untuk menginisialisasi-awal nilai (misalnya jika nilai Anda adalah daftar, misalnya, dan Anda ingin memastikan bahwa ada daftar kosong yang dapat Anda tambahkan saat memasukkan nilai pertama untuk kunci). Dalam kasus seperti itu, Anda mungkin menemukancollections.defaultdict() jenis yang menarik.

Dalam kode yang lebih lama, Anda mungkin juga menemukan beberapa kegunaan dari has_key(), metode yang sudah usang untuk memeriksa keberadaan kunci dalam kamus (gunakan saja key_name in dict_name, sebagai gantinya).


2
Ingin berbagi (menggunakan Python 2.7) waktu berjalan dari sesuatu yang baru saja saya tulis, mendasarkan pada dicts, adalah 363.235070 menggunakan "key di dict.keys ()" dan secara drastis turun ke 0,260186 hanya dengan menghapus panggilan untuk kunci "( ) "
Ido_f

@Ido_f tolong posting tolok ukur Anda, karena tolok ukur saya hampir tidak ada perbedaan dalam 3,5 dan 2,7
Charles Addis

@ Ido_f Saya menduga itu adalah sesuatu yang lain di program Anda yang merupakan sesuatu yang lain, tetapi sebenarnya tidak key in dict.keys(). Coba hapus semua kode kecuali untuk pemeriksaan ini dan lihat apa hasilnya.
Charles Addis

101

Anda dapat mempersingkat ini:

if 'key1' in dict:
    ...

Namun, ini merupakan perbaikan kosmetik terbaik. Mengapa Anda percaya ini bukan cara terbaik?


100
Ini jauh lebih dari perbaikan kosmetik. Waktu untuk menemukan kunci menggunakan metode ini adalah O (1) sedangkan kunci panggilan akan menghasilkan daftar dan menjadi O (n).
Jason Baker

5
O (1) sepertinya tidak tepat. Apakah Anda yakin itu bukan sesuatu seperti O (log n)?
spektrum

12
Ini kompleksitas pencarian dict tunggal, yang rata-rata O (1) dan paling buruk O (n). .list () akan selalu menjadi O (n). wiki.python.org/moin/TimeComplexity
Leonora Tindall

1
ini juga menghindari alokasi tambahan. (penting untuk membuat loop ketat sedikit lebih cepat)
nurettin

57

Untuk info tambahan tentang eksekusi cepat dari metode yang diusulkan pada jawaban yang diterima (putaran 10m):

  • 'key' in mydict waktu yang terlewati 1,07 detik
  • mydict.get('key') waktu yang terlewati 1,84 detik
  • mydefaultdict['key'] waktu yang terlewati 1,07 detik

Oleh karena itu menggunakan inatau defaultdictdisarankan untuk tidak get.


6
setuju sepenuhnya bahwa get1,84 adalah <1,07 * 2 ;-P
Paul Rigor

54

Saya akan merekomendasikan menggunakan setdefaultmetode ini sebagai gantinya. Sepertinya itu akan melakukan semua yang Anda inginkan.

>>> d = {'foo':'bar'}
>>> q = d.setdefault('foo','baz') #Do not override the existing key
>>> print q #The value takes what was originally in the dictionary
bar
>>> print d
{'foo': 'bar'}
>>> r = d.setdefault('baz',18) #baz was never in the dictionary
>>> print r #Now r has the value supplied above
18
>>> print d #The dictionary's been updated
{'foo': 'bar', 'baz': 18}

9
Apa yang setdefaultharus dilakukan dengan pertanyaan OP?
hughdbrown

18
@ hughdbrown "Saya ingin menguji apakah ada kunci dalam kamus sebelum memperbarui nilai untuk kunci." Terkadang posting menyertakan kode yang menghasilkan kesibukan tanggapan terhadap sesuatu yang bukan tujuan awal. Untuk mencapai tujuan yang dinyatakan dalam kalimat pertama, setdefault adalah metode yang paling efektif, meskipun itu bukan pengganti drop-in untuk kode sampel yang diposting.
David Berger

5
Ini adalah jawaban yang unggul karena memenuhi tujuan OP bukan hanya memberikan jawaban yang benar secara teknis. Lihat: nedbatchelder.com/blog/201207/…
Niels Bom

+1 untuk jawaban informatif, yang mengajarkan saya sesuatu. Namun, apakah itu solusi terbaik tergantung pada apa yang ada dalam pikiran sang pembuat kode; misalnya arti "sebelum memperbarui nilai kunci". Mungkin dia akan melempar pengecualian jika tidak ada (== tidak ada izin untuk menambahkan kunci baru). Mungkin ini adalah kamus hitungan, dan dia akan menambahkan 1 ke jumlah yang ada, dalam hal ini `d [kunci] = d.get (kunci, 0) +1 adalah solusi paling bersih (seperti yang ditunjukkan Chris, setelah jawaban Anda) Sudah ditulis). (Saya hanya repot menyebutkan ini, kalau-kalau pembaca mendatang datang ke sini, dengan tugas yang berbeda dalam pikiran.)
ToolmakerSteve

1
@ToolmakerSteve Benar. Masalahnya di sini adalah bahwa pertanyaan OP tidak cukup jelas.
Niels Bom

45

Kamus dengan python memiliki metode get ('key', default). Jadi Anda bisa mengatur nilai default jika tidak ada kunci.

values = {...}
myValue = values.get('Key', None)

33

Bagaimana dengan menggunakan EAFP (lebih mudah untuk meminta maaf daripada izin):

try:
   blah = dict["mykey"]
   # key exists in dict
except KeyError:
   # key doesn't exist in dict

Lihat posting SO lainnya:

Menggunakan try vs if dalam python atau

Memeriksa keberadaan anggota dalam Python


12
Coba / kecuali mungkin lebih mahal jika kemungkinan kunci itu sering tidak ada. Dari pos yang Anda referensikan: "[Saya] jika Anda berharap 99% dari hasil waktu akan benar-benar mengandung sesuatu yang dapat diubah, saya akan menggunakan pendekatan coba / kecuali. Akan lebih cepat jika pengecualian benar-benar luar biasa. Jika hasilnya Tidak Ada lebih dari 50% dari waktu, kemudian menggunakan jika mungkin lebih baik. [...] [A] dan jika pernyataan selalu merugikan Anda, hampir bebas untuk membuat blok coba / kecuali. Tetapi ketika Pengecualian benar-benar terjadi, biaya jauh lebih tinggi. " stackoverflow.com/a/1835844/1094092
billrichards

28

Menggunakan operator ternary:

message = "blah" if 'key1' in dict else "booh"
print(message)

20

Cara di mana Anda bisa mendapatkan hasilnya adalah:

Mana yang lebih baik tergantung pada 3 hal:

  1. Apakah kamus 'biasanya memiliki kunci' atau 'biasanya tidak memiliki kunci'.
  2. Apakah Anda berniat menggunakan kondisi seperti jika ... yang lain ... yang lain jika ... yang lain?
  3. Seberapa besar kamus?

Baca lebih banyak: http://paltman.com/try-except-performance-in-python-a-simple-test/

Gunakan try / block alih-alih 'in' atau 'if':

try:
    my_dict_of_items[key_i_want_to_check]
except KeyError:
    # Do the operation you wanted to do for "key not present in dict".
else:
    # Do the operation you wanted to do with "key present in dict."

2
Bagus tapi perlu diaktualisasikan untuk python 3. Saya mengkonversi skrip halaman web dengan 2to3, dan melihat bahwa sintaks tanpa coba selalu lebih cepat daripada dengan sintaks dengan try, bahkan dalam kasus di mana kunci berada di dikt.
Jean Paul

18

Hanya Python 2: (dan insudah mendukung python 2.7 )

Anda dapat menggunakan metode has_key ():

if dict.has_key('xyz')==1:
    #update the value for the key
else:
    pass

22
.has_key()telah ditinggalkan ; Anda harus menggunakan inseperti yang ditunjukkan pada jawaban lain.
Brad Koch

12
BTW, saya sarankan membaca SEMUA jawaban yang ada untuk pertanyaan LAMA , sebelum menjawabnya. Jawaban ini tidak menambahkan apa pun, karena saran itu sudah ada dalam jawaban Michael, mulai '09. (Saya tidak bermaksud untuk mencegah upaya untuk menambahkan sesuatu yang berguna untuk diskusi. Terus mencoba.)
ToolmakerSteve

16

Hanya FYI yang menambah Chris. B (jawaban terbaik):

d = defaultdict(int)

Bekerja juga; alasannya adalah bahwa panggilan int()kembali 0adalah apa yang defaultdictdilakukan di belakang layar (saat membuat kamus), maka nama "Fungsi Pabrik" dalam dokumentasi.


2
Jika Anda membuat kamus jumlah, Anda harus menggunakan Penghitung (dengan asumsi Python 2.7). Dan saya menggunakan defaultdict(lambda: 0)bukan defaultdict(int)karena saya pikir itu lebih jelas apa yang terjadi; pembaca tidak perlu tahu Anda dapatkan 0jika Anda menelepon int()tanpa argumen. YMMV.
Chris B.

9

Periksa apakah kunci yang diberikan sudah ada dalam kamus

Untuk mendapatkan ide bagaimana melakukannya, pertama-tama kita memeriksa metode apa yang bisa kita panggil dalam kamus. Berikut adalah metodenya:

d={'clear':0, 'copy':1, 'fromkeys':2, 'get':3, 'items':4, 'keys':5, 'pop':6, 'popitem':7, 'setdefault':8, 'update':9, 'values':10}

Python Dictionary clear()       Removes all Items
Python Dictionary copy()        Returns Shallow Copy of a Dictionary
Python Dictionary fromkeys()    Creates dictionary from given sequence
Python Dictionary get()         Returns Value of The Key
Python Dictionary items()       Returns view of dictionary (key, value) pair
Python Dictionary keys()        Returns View Object of All Keys
Python Dictionary pop()         Removes and returns element having given key
Python Dictionary popitem()     Returns & Removes Element From Dictionary
Python Dictionary setdefault()  Inserts Key With a Value if Key is not Present
Python Dictionary update()      Updates the Dictionary 
Python Dictionary values()      Returns view of all values in dictionary

Metode brutal untuk memeriksa apakah kunci sudah ada mungkin get()metode:

d.get("key")

Dua metode lain yang menarikitems() dan keys()kedengarannya seperti terlalu banyak pekerjaan. Jadi mari kita periksa apakah get()metode yang tepat untuk kita. Kami memiliki dict kami d:

d= {'clear':0, 'copy':1, 'fromkeys':2, 'get':3, 'items':4, 'keys':5, 'pop':6, 'popitem':7, 'setdefault':8, 'update':9, 'values':10}

Pencetakan menunjukkan kunci yang tidak kita miliki akan kembali None:

print(d.get('key')) #None
print(d.get('clear')) #0
print(d.get('copy')) #1

Kami dapat menggunakannya untuk mendapatkan info jika kunci ada atau tidak. Tetapi pertimbangkan ini jika kita membuat dict dengan satu key:None:

d= {'key':None}
print(d.get('key')) #None
print(d.get('key2')) #None

Memimpin get()metode itu tidak dapat diandalkan jika beberapa nilai mungkin None. Kisah ini seharusnya memiliki akhir yang lebih bahagia. Jika kita menggunakan inpembanding:

print('key' in d) #True
print('key2' in d) #False

Kami mendapatkan hasil yang benar. Kami dapat memeriksa kode byte Python:

import dis
dis.dis("'key' in d")
#   1           0 LOAD_CONST               0 ('key')
#               2 LOAD_NAME                0 (d)
#               4 COMPARE_OP               6 (in)
#               6 RETURN_VALUE

dis.dis("d.get('key2')")
#   1           0 LOAD_NAME                0 (d)
#               2 LOAD_METHOD              1 (get)
#               4 LOAD_CONST               0 ('key2')
#               6 CALL_METHOD              1
#               8 RETURN_VALUE

Ini menunjukkan bahwa inoperator pembanding tidak hanya lebih andal tetapi bahkan lebih cepat daripada get().


.get()dapat memiliki argumen kedua untuk defaultnilai, yang bisa digunakan untuk menangani masalah di mana key:None. contoh: d.get("key", False)
Alex

.get()adalah cara tercepat. Pilihan lain adalah untuk menetapkan di try/ exceptblock
HCLivess

7

Kamus Python memiliki metode yang disebut __contains__. Metode ini akan mengembalikan True jika kamus memiliki kunci lain mengembalikan False.

 >>> temp = {}

 >>> help(temp.__contains__)

Help on built-in function __contains__:

__contains__(key, /) method of builtins.dict instance
    True if D has a key k, else False.

2
Merupakan praktik yang sangat buruk untuk menelepon __contains__langsung. Cara yang benar untuk melakukannya, adalah menggunakan inoperator, yang containment checkmemanggil __contains__fungsi.
user1767754

@ user1767754 Saya menggunakan foo = x['foo'] if x.__contains__('foo') else 'bar'. Adakah ide bagaimana menggunakan inoperator sebagai bagian dari ungkapan ini?
donrondadon

1
foo = x['foo'] if 'foo' in x else 'bar'
Ray Wu

5

Berbagi satu lagi cara memeriksa apakah ada kunci menggunakan operator boolean.

d = {'a': 1, 'b':2}
keys = 'abcd'

for k in keys:
    x = (k in d and 'blah') or 'boo'
    print(x) 

Ini kembali

>>> blah
>>> blah
>>> boo
>>> boo

Penjelasan

Pertama, Anda harus tahu bahwa di Python, 0, None, atau benda dengan panjang nol mengevaluasi False. Segala sesuatu yang lain dievaluasiTrue . Operasi Boolean dievaluasi dari kiri ke kanan dan mengembalikan operan tidak Benar atau Salah.

Mari kita lihat sebuah contoh:

>>> 'Some string' or 1/0 
'Some string'
>>>

Sejak 'Some string'mengevaluasi hingga True, sisanyaor tidak dievaluasi dan tidak ada pembagian dengan kesalahan nol yang diajukan.

Tetapi jika kita beralih, urutannya 1/0dievaluasi terlebih dahulu dan menimbulkan pengecualian:

>>> 1/0 or 'Some string'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
>>> 

Kita bisa menggunakan ini untuk pola untuk memeriksa apakah ada kunci.

(k in d and 'blah')

melakukan hal yang sama dengan

if k in d:
    'blah'
else:
    False

Ini sudah mengembalikan hasil yang benar jika kunci ada, tetapi kami ingin itu mencetak 'boo' ketika tidak. Jadi, kami ambil hasilnya dan ordengan'boo'

>>> False or 'boo'
'boo'
>>> 'blah' or 'boo'
'blah'
>>> 

1

Anda dapat menggunakan forloop untuk beralih ke kamus dan mendapatkan nama kunci yang ingin Anda temukan di kamus, setelah itu periksa apakah ada atau tidak menggunakan ifkondisi:

dic = {'first' : 12, 'second' : 123}
for each in dic:
    if each == 'second': 
        print('the key exists and the corresponding value can be updated in the dictionary')

periksa kode karena output untuk ini adalah it is existdannot exist
system123456

mengapa menggunakan kamus sama sekali jika ini adalah untuk melakukan pencarian linear?
Jean-François Fabre
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.