Bagaimana cara saya mengecek bahwa beberapa kunci berada dalam dict dalam satu pass?

218

Saya ingin melakukan sesuatu seperti:

foo = {'foo':1,'zip':2,'zam':3,'bar':4}

if ("foo","bar") in foo:
    #do stuff

Bagaimana cara saya mengecek apakah 'foo' dan 'bar' keduanya dalam dict foo?

Jean-François Corbett
sumber

Jawaban:

363

Nah, Anda bisa melakukan ini:

>>> if all (k in foo for k in ("foo","bar")):
...     print "They're there!"
...
They're there!
hughdbrown
sumber
10
+1, saya suka ini lebih baik daripada jawaban Greg karena lebih ringkas dan lebih cepat (tidak ada daftar sementara yang tidak relevan, DAN eksploitasi penuh hubungan pendek).
Alex Martelli
4
Saya suka semua () dan any (). Mereka membuat begitu banyak algoritma sehingga lebih bersih.
hughdbrown
Saya akhirnya menggunakan solusi ini. Tampaknya yang terbaik untuk kumpulan data yang lebih besar. Saat memeriksa katakanlah 25 atau 30 kunci.
4
Ini adalah solusi yang bagus berkat hubungan arus pendek, terutama jika tes gagal lebih sering daripada tidak; kecuali Anda dapat membuat set kunci yang menarik hanya sekali dan memeriksanya berkali-kali, dalam hal setini lebih baik. Seperti biasa ... mengukurnya! -)
Alex Martelli
Saya menggunakan ini setiap kali terlihat lebih bagus daripada cara "normal", dengan semua dan atau atau ... itu juga bagus karena Anda dapat menggunakan "semua" atau "apa pun" ... selain itu Anda juga dapat memiliki " k in foo "atau" k not in foo "tergantung pada tes yang Anda coba lakukan
Terence Honles
123
if {"foo", "bar"} <= myDict.keys(): ...

Jika Anda masih menggunakan Python 2, Anda bisa melakukannya

if {"foo", "bar"} <= myDict.viewkeys(): ...

Jika Anda masih menggunakan Python yang sangat lama <= 2.6, Anda dapat memanggil setdict, tetapi itu akan beralih ke seluruh dict untuk membangun set, dan itu lambat:

if set(("foo", "bar")) <= set(myDict): ...
Alex Martelli
sumber
kelihatan bagus! Satu-satunya hal yang saya tidak suka adalah Anda harus membuat set sementara, tetapi sangat kompak. Jadi saya harus mengatakan ... penggunaan set yang bagus!
Terence Honles
17
Dalam python 3 Anda bisa mengatakan set(("foo","bar")) <= myDict.keys()yang menghindari set sementara, jadi jauh lebih cepat. Untuk pengujian saya, kecepatannya hampir sama dengan menggunakan semua ketika kueri adalah 10 item. Semakin lambat karena kueri semakin besar.
John La Rooy
1
Saya telah memposting beberapa tes saya sebagai jawaban. stackoverflow.com/questions/1285911/…
John La Rooy
30
if {'foo', 'bar'} <= set(myDict): ...
Boris Raicheff
11
Bagi siapa pun yang bertanya-tanya mengapa ini bekerja: operator <= sama dengan menggunakan metode .set issubset (): docs.python.org/3/library/stdtypes.html#set-types-set-frozenset
edepe
41

Rig pembandingan sederhana untuk 3 alternatif.

Masukkan nilai Anda sendiri untuk D dan Q


>>> from timeit import Timer
>>> setup='''from random import randint as R;d=dict((str(R(0,1000000)),R(0,1000000)) for i in range(D));q=dict((str(R(0,1000000)),R(0,1000000)) for i in range(Q));print("looking for %s items in %s"%(len(q),len(d)))'''

>>> Timer('set(q) <= set(d)','D=1000000;Q=100;'+setup).timeit(1)
looking for 100 items in 632499
0.28672504425048828

#This one only works for Python3
>>> Timer('set(q) <= d.keys()','D=1000000;Q=100;'+setup).timeit(1)
looking for 100 items in 632084
2.5987625122070312e-05

>>> Timer('all(k in d for k in q)','D=1000000;Q=100;'+setup).timeit(1)
looking for 100 items in 632219
1.1920928955078125e-05
John La Rooy
sumber
4
Python 2.7 harus d.viewkeys()dibuat set(q) <= d.viewkeys().
Martijn Pieters
Python 2.7.5punya d.keys()metode juga.
Ivan Kharlamov
3
@IvanKharlamov, tetapi dalam Python2, itu tidak mengembalikan objek yang kompatibel denganset(q) <= ...
John La Rooy
1
Buruk saya, Anda benar-benar tepat: itu kembali TypeError: can only compare to a set. Maaf! :))
Ivan Kharlamov
1
Untuk Python 2 beralih perintah: d.viewkeys() >= set(q). Saya datang ke sini mencoba mencari tahu mengapa pesanan itu penting!
Veedrac
34

Anda tidak harus membungkus sisi kiri dalam satu set. Anda bisa melakukan ini:

if {'foo', 'bar'} <= set(some_dict):
    pass

Ini juga berkinerja lebih baik daripada all(k in d...)solusinya.

claytonk
sumber
2
Ini juga berkinerja lebih baik daripada semua (k dalam d ...) solusi. Saya menyarankan ini sebagai edit, tetapi ditolak dengan alasan lebih baik menambahkan komentar . Jadi inilah saya melakukan hal itu
miraculixx
@miraculixx Tidak lebih baik menambahkan komentar. Lebih baik mengedit informasi yang relevan menjadi jawaban dan menghapus komentar.
endolith
1
@endolith Saya setuju, beberapa orang jelas tidak seperti yang Anda lihat di edit ditolak yang saya lakukan di tempat pertama. Pokoknya itu diskusi untuk meta bukan untuk di sini.
miraculixx
Bisakah seseorang menjelaskan hal ini? Saya telah mengumpulkan bahwa {} menciptakan suatu himpunan, tetapi bagaimana operator yang kurang setara atau sama bekerja di sini?
Locane
1
@Locane The <= tes operator jika set pertama adalah subset dari set kedua. Anda juga dapat melakukan {'foo', 'bar'}. Issubset (somedict). Dokumentasi untuk set metodologi dapat ditemukan di sini: docs.python.org/2/library/sets.html
Meow
24

Menggunakan set :

if set(("foo", "bar")).issubset(foo):
    #do stuff

Kalau tidak:

if set(("foo", "bar")) <= set(foo):
    #do stuff
Karl Voigtland
sumber
2
set (d) seperti yang saya gunakan dalam jawaban saya sama seperti set (d.keys ()) tetapi lebih cepat, lebih pendek, dan saya akan mengatakan lebih disukai secara gaya.
Alex Martelli
set(d)adalah sama dengan set(d.keys())(tanpa daftar perantara yang d.keys()membangun)
Jochen Ritzel
11

Bagaimana dengan ini:

if all([key in foo for key in ["foo","bar"]]):
    # do stuff
    pass
Greg
sumber
8
memang, tidak hanya tidak perlu, secara positif berbahaya, karena mereka menghalangi perilaku hubungan arus pendek normal all.
Alex Martelli
10

Saya pikir ini adalah yang paling cerdas dan pithonic.

{'key1','key2'} <= my_dict.keys()
Shota Tamura
sumber
9

Sementara saya suka jawaban Alex Martelli, sepertinya tidak Pythonic bagi saya. Artinya, saya pikir bagian penting dari menjadi Pythonic adalah agar mudah dimengerti. Dengan tujuan itu, <=tidak mudah dipahami.

Meskipun lebih banyak karakter, menggunakan issubset()seperti yang disarankan oleh jawaban Karl Voigtland lebih dimengerti. Karena metode itu dapat menggunakan kamus sebagai argumen, solusi pendek dan mudah dipahami adalah:

foo = {'foo': 1, 'zip': 2, 'zam': 3, 'bar': 4}

if set(('foo', 'bar')).issubset(foo):
    #do stuff

Saya ingin menggunakannya {'foo', 'bar'}sebagai pengganti set(('foo', 'bar')), karena lebih pendek. Namun, itu tidak bisa dimengerti dan saya pikir kawat gigi terlalu mudah dikacaukan sebagai kamus.

LS
sumber
2
Saya pikir ini bisa dimengerti begitu Anda mengerti artinya.
Bobort
Ada dalam dokumentasi sebagai sinonim untuk .issubset(). Saya pikir berada dalam dokumentasi Python membuatnya menjadi Pythonic secara default.
ingyhere
4

Solusi Alex Martelli set(queries) <= set(my_dict)adalah kode terpendek tetapi mungkin bukan yang tercepat. Asumsikan Q = len (kueri) dan D = len (my_dict).

Ini membutuhkan O (Q) + O (D) untuk membuat dua set, dan kemudian (satu harapan!) Hanya O (min (Q, D)) untuk melakukan tes subset - dengan asumsi tentu saja Python mengatur pencarian. adalah O (1) - ini adalah kasus terburuk (ketika jawabannya adalah Benar).

Solusi generator hughdbrown (et al?) all(k in my_dict for k in queries)Adalah yang terburuk O (Q).

Faktor-faktor yang rumit:
(1) loop pada gadget berbasis set semua dilakukan pada kecepatan-C sedangkan gadget berbasis apa pun adalah perulangan dengan bytecode.
(2) Penelepon gadget berbasis apa pun mungkin dapat menggunakan pengetahuan tentang kemungkinan kegagalan untuk memesan item permintaan yang sesuai sedangkan gadget berbasis set tidak memungkinkan kontrol seperti itu.

Seperti biasa, jika kecepatan itu penting, pembandingan dengan kondisi operasional adalah ide yang bagus.

John Machin
sumber
1
Generator lebih cepat untuk semua case yang saya coba. stackoverflow.com/questions/1285911/…
John La Rooy
2

Anda dapat menggunakan .issubset () juga

>>> {"key1", "key2"}.issubset({"key1":1, "key2":2, "key3": 3})
True
>>> {"key4", "key2"}.issubset({"key1":1, "key2":2, "key3": 3})
False
>>>
Sinan Çetinkaya
sumber
1

Bagaimana kalau menggunakan lambda?

 if reduce( (lambda x, y: x and foo.has_key(y) ), [ True, "foo", "bar"] ): # do stuff
Jinuk Kim
sumber
2
Jawaban ini adalah satu-satunya yang benar secara fungsional yang akan bekerja pada Python 1.5 dengan perubahan sederhana (s / True / 1 /) ... tetapi tidak ada yang bisa dilakukan untuk itu. DAN True thingy akan lebih baik sebagai arg initializer opsional daripada menjejalkan ke depan arg urutan.
John Machin
1

Jika Anda ingin:

  • juga dapatkan nilai untuk kunci
  • periksa lebih dari satu kamus

kemudian:

from operator import itemgetter
foo = {'foo':1,'zip':2,'zam':3,'bar':4}
keys = ("foo","bar") 
getter = itemgetter(*keys) # returns all values
try:
    values = getter(foo)
except KeyError:
    # not both keys exist
    pass
Jochen Ritzel
sumber
1

Bukan untuk menyarankan bahwa ini bukan sesuatu yang belum Anda pikirkan, tetapi saya menemukan bahwa hal yang paling sederhana biasanya yang terbaik:

if ("foo" in foo) and ("bar" in foo):
    # do stuff
Jason Baker
sumber
1
>>> if 'foo' in foo and 'bar' in foo:
...     print 'yes'
... 
yes

Jason, () tidak perlu dalam Python.

Juanjo Conti
sumber
3
Masih mereka mungkin gaya yang baik ... tanpa mereka, otak C + + - saya selalu bertanya-tanya apakah itu akan ditafsirkan sebagai "jika 'foo in (foo dan' bar ') di foo:"
Jeremy Friesner
1
Saya mengerti bahwa itu tidak perlu. Saya hanya merasa bahwa mereka menambah kejelasan dalam hal ini.
Jason Baker
0

Hanya saya ambil ini, ada dua metode yang mudah dimengerti dari semua opsi yang diberikan. Jadi kriteria utama saya adalah memiliki kode yang sangat mudah dibaca, bukan kode yang sangat cepat. Agar kode dapat dimengerti, saya lebih suka memberi kemungkinan:

  • var <= var2.keys ()
  • var.issubset (var2)

Fakta bahwa "var <= var2.keys ()" mengeksekusi lebih cepat dalam pengujian saya di bawah ini, saya lebih suka yang ini.

import timeit

timeit.timeit('var <= var2.keys()', setup='var={"managed_ip", "hostname", "fqdn"}; var2= {"zone": "test-domain1.var23.com", "hostname": "bakje", "api_client_ip": "127.0.0.1", "request_data": "", "request_method": "GET", "request_url": "hvar2p://127.0.0.1:5000/test-domain1.var23.com/bakje", "utc_datetime": "04-Apr-2019 07:01:10", "fqdn": "bakje.test-domain1.var23.com"}; var={"managed_ip", "hostname", "fqdn"}')
0.1745898080000643

timeit.timeit('var.issubset(var2)', setup='var={"managed_ip", "hostname", "fqdn"}; var2= {"zone": "test-domain1.var23.com", "hostname": "bakje", "api_client_ip": "127.0.0.1", "request_data": "", "request_method": "GET", "request_url": "hvar2p://127.0.0.1:5000/test-domain1.var23.com/bakje", "utc_datetime": "04-Apr-2019 07:01:10", "fqdn": "bakje.test-domain1.var23.com"}; var={"managed_ip", "hostname", "fqdn"};')
0.2644960229999924
PietjePuk
sumber
0

Dalam hal menentukan apakah hanya beberapa kunci yang cocok, ini berfungsi:

any_keys_i_seek = ["key1", "key2", "key3"]

if set(my_dict).intersection(any_keys_i_seek):
    # code_here
    pass

Namun pilihan lain untuk menemukan jika hanya beberapa kunci yang cocok:

any_keys_i_seek = ["key1", "key2", "key3"]

if any_keys_i_seek & my_dict.keys():
    # code_here
    pass
di sana
sumber
0

Opsi lain untuk mendeteksi apakah semua kunci ada dict:

dict_to_test = { ... }  # dict
keys_sought = { "key_sought_1", "key_sought_2", "key_sought_3" }  # set

if keys_sought & dict_to_test.keys() == keys_sought: 
    # yes -- dict_to_test contains all keys in keys_sought
    # code_here
    pass
di sana
sumber
-4
>>> ok
{'five': '5', 'two': '2', 'one': '1'}

>>> if ('two' and 'one' and 'five') in ok:
...   print "cool"
... 
cool

Ini sepertinya berhasil

Prashanth Gowda
sumber
Ini pintar dan saya yakin itu tidak berhasil sampai saya mencobanya sendiri. Saya curiga ()akan dievaluasi terlebih dahulu dan menghasilkan True, yang kemudian akan memeriksa apakah True in ok. Bagaimana cara kerjanya?
durden2.0
7
('dua' dan 'satu' dan 'lima') mengembalikan 'lima', sehingga sebenarnya hanya memeriksa jika 'lima' ada dict
HardQuestions