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?

Jawaban:


363

Nah, Anda bisa melakukan ini:

>>> if all (k in foo for k in ("foo","bar")):
...     print "They're there!"
...
They're there!

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): ...

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

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.


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

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

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()

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.


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.


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
>>>

1

Bagaimana kalau menggunakan lambda?

 if reduce( (lambda x, y: x and foo.has_key(y) ), [ True, "foo", "bar"] ): # do stuff

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

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

1
>>> if 'foo' in foo and 'bar' in foo:
...     print 'yes'
... 
yes

Jason, () tidak perlu dalam Python.


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

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

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

-4
>>> ok
{'five': '5', 'two': '2', 'one': '1'}

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

Ini sepertinya berhasil


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
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.