Tek bir geçişte birden çok anahtarın dikte olup olmadığını nasıl kontrol edebilirim?


218

Gibi bir şey yapmak istiyorum:

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

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

Hem 'foo' hem de 'bar' ın dikte foo olup olmadığını nasıl kontrol edebilirim?

Yanıtlar:


363

Bunu yapabilirsin:

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

10
+1, bunu Greg'in cevabından daha çok beğeniyorum çünkü daha özlü VE daha hızlı (alakasız geçici liste oluşturma ve kısa devreden tam olarak yararlanma).
Alex Martelli

4
Hepsini () ve herhangi birini () seviyorum. Birçok algoritmayı çok daha temiz hale getiriyorlar.
hughdbrown

Sonunda bu çözümü kullandım. Daha büyük veri kümeleri için en iyisi gibi görünüyordu. 25 veya 30 anahtar diyelim.

4
Kısa devre sayesinde, özellikle de test daha sık başarısız olursa iyi bir çözümdür; ilgilenen anahtarlar kümesini sadece bir kez oluşturamaz ve birçok kez kontrol edemezseniz, bu durumda setüstündür. Her zamanki gibi ... ölçün! -)
Alex Martelli

Bunu "normal" yoldan daha güzel göründüğünde kullanıyorum, tüm ve ya da ya da 's ... hem de "ya" ya da "herhangi bir" kullanabilirsiniz ... k "foo" veya "k değil foo" yapmaya çalıştığınız teste bağlı olarak
Terence Honles

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

Hala Python 2'de iseniz, şunları yapabilirsiniz

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

Hala gerçekten eski bir Python <= 2.6 kullanıyorsanız, setdikteyi çağırabilirsiniz , ancak seti oluşturmak için tüm dikteyi yineleyecektir ve bu yavaş:

if set(("foo", "bar")) <= set(myDict): ...

iyi görünüyor! Sevmediğim tek şey geçici setler oluşturmanız gerektiğidir, ancak çok kompakttır. Yani söylemeliyim ki ... setlerin güzel kullanımı!
Terence Honles

17
Python 3'te set(("foo","bar")) <= myDict.keys()geçici setten kaçındığını söyleyebilirsiniz , bu yüzden çok daha hızlıdır. Testlerim için, sorgu 10 öğe olduğunda hepsini kullanmakla aynı hızda. Sorgu daha da büyüdükçe yavaşlar.
John La Rooy

1
Bazı testlerimi cevap olarak gönderdim. stackoverflow.com/questions/1285911/…
John La Rooy

30
if {'foo', 'bar'} <= set(myDict): ...
Boris Raicheff

11
Bunun neden çalıştığını merak eden herkes için: <= işleci, use .set issubset () yöntemiyle aynıdır
edepe

41

3 alternatif için basit kıyaslama donanımı.

D ve Q için kendi değerlerinizi girin


>>> 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 d.viewkeys()yapmak zorunda set(q) <= d.viewkeys().
Martijn Pieters

Python 2.7.5vardır d.keys()da yöntem.
Ivan Kharlamov

3
@IvanKharlamov, ancak Python2'de, uyumlu bir nesne döndürmüyorset(q) <= ...
John La Rooy

1
Benim hatam, kesinlikle dikkat edersiniz: geri döner TypeError: can only compare to a set. Afedersiniz! :))
Ivan Kharlamov

1
Python 2 için sırasını değiştirmek: d.viewkeys() >= set(q). Buraya siparişin neden önemli olduğunu bulmaya çalıştım!
Veedrac

34

Sol tarafı bir sete sarmanıza gerek yok. Bunu sadece yapabilirsiniz:

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

Bu aynı zamanda all(k in d...)çözümden daha iyi performans gösterir .


2
Bu aynı zamanda hepsi (k in d ...) çözeltisinden daha iyi performans gösterir. Bunu bir düzenleme olarak önerdim, ancak yorum eklemek daha iyi olduğu gerekçesiyle reddedildi . İşte bana bunu yapıyorum
miraculixx

@miraculixx Yorum eklemek daha iyi değil. İlgili bilgileri bir yanıtta düzenlemek ve yorumları silmek daha iyidir.
Endolit

1
@endolith Kabul ediyorum, bazı insanlar ilk etapta yaptığım reddedilen düzenlemede gördüğünüz gibi değil. Her neyse, bu meta için bir tartışma değil.
miraculixx

Birisi bunu açıklayabilir mi lütfen? {} Bir küme oluşturduğunu topladım, ancak burada eşit veya eşit olmayan işleç nasıl çalışıyor?
Locane

1
@Locane <= operatörü, ilk kümenin ikinci kümenin bir alt kümesi olup olmadığını test eder. Ayrıca {'foo', 'bar'} yapabilirsiniz. İssubset (somedict). Belirlenen metodoloji için dokümantasyona buradan ulaşabilirsiniz: docs.python.org/2/library/sets.html
Miyav

24

Kullanılması setleri :

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

Alternatif:

if set(("foo", "bar")) <= set(foo):
    #do stuff

2
cevabımda kullandığım gibi set (d), set (d.keys ()) gibi ama daha hızlı, daha kısa ve stilistik olarak tercih edilebilir diyebilirim.
Alex Martelli

set(d)ile aynıdır set(d.keys())( d.keys()inşa eden ara liste olmadan )
Jochen Ritzel

11

Buna ne dersin:

if all([key in foo for key in ["foo","bar"]]):
    # do stuff
    pass

8
aslında, sadece gereksiz değil, aynı zamanda normal kısa devre davranışını engellediği için de zararlı all.
Alex Martelli


9

Alex Martelli'nin cevabını beğenmeme rağmen, bana Pythonic gibi gelmiyor. Yani, Pythonic olmanın önemli bir kısmının kolayca anlaşılabilir olması gerektiğini düşündüm. Bu amaçla, <=anlaşılması kolay değil.

Daha fazla karakter olsa da, issubset()Karl Voigtland'ın cevabının önerdiği şekilde kullanmak daha anlaşılır. Bu yöntem sözlüğü bağımsız değişken olarak kullanabildiğinden, kısa ve anlaşılabilir bir çözümdür:

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

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

Ben kullanmak istiyorum {'foo', 'bar'}yerine set(('foo', 'bar'))daha kısa olduğu için. Ancak, bu anlaşılabilir değil ve braces bir sözlük olmak çok kolay karıştı düşünüyorum.


2
Ne anlama geldiğini anladıktan sonra bunun anlaşılabilir olduğunu düşünüyorum.
Bobort

Bu eşanlamlı olarak belgelerinde.issubset() . Bence Python belgelerinde olmak varsayılan olarak Pythonic yapar.
ingyhere

4

Alex Martelli'nin çözümü set(queries) <= set(my_dict) en kısa koddur, ancak en hızlı kod olmayabilir. Q = len (sorgular) ve D = len (my_dict) olduğunu varsayın.

Bu, iki seti yapmak için O (Q) + O (D) ve sonra alt set testi yapmak için sadece O (min (Q, D)) gerektirir - tabii ki Python setinin aramasını varsayarsak O (1) - bu en kötü durumdur (cevap Doğru olduğunda).

Hughdbrown (et al?) 'Nin jeneratör çözeltisi all(k in my_dict for k in queries)en kötü durum O (Q)' dur .

Karmaşık faktörler:
(1) set tabanlı gadget'taki döngülerin tümü C hızında yapılırken, herhangi bir tabanlı gadget bayt kodu üzerinde döngü yapar.
(2) Herhangi bir tabanlı aracın arayanı, sorgu öğelerini buna göre sıralayamamak için herhangi bir hata olasılığı bilgisini kullanabilirken, set tabanlı araç böyle bir kontrole izin vermez.

Her zaman olduğu gibi, hız önemliyse, operasyonel koşullar altında kıyaslama yapmak iyi bir fikirdir.


1
Jeneratör denediğim tüm durumlar için daha hızlıydı. stackoverflow.com/questions/1285911/…
John La Rooy

2

Sen kullanabilirsiniz ) (.issubset yanı

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

1

Lambda kullanmaya ne dersiniz?

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

2
Bu cevap, Python 1.5 üzerinde basit bir değişiklikle (s / True / 1 /) çalışacak olan işlevsel olarak doğru olan tek cevaptır ... ancak bunun için başka bir şey yoktur. VE Gerçek şey, dizi argümanının önüne sıkışmak yerine isteğe bağlı başlatıcı arg olarak daha iyi olurdu.
John Machin

1

İsterseniz:

  • ayrıca tuşların değerlerini al
  • birden fazla dikoneri kontrol et

sonra:

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

Bunun aklınıza gelmediği bir şey olmadığını öne sürmek değil, ama en basit şeyin genellikle en iyisi olduğunu görüyorum:

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

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

Jason, () Python'da gerekli değildir.


3
: O "foo içinde '(bar 'foo ve) foo' eğer" olarak yorumlanabilir olacak eğer addled beyin hep harikalar - Yine onlar onlarsız, benim C ++ ... iyi tarzı olabilir
Jeremy Friesner

1
Gerekli olmadıklarını anlıyorum. Sadece bu durumda netlik eklediklerini hissediyorum.
Jason Baker

0

Sadece bunu benim, verilen tüm seçenekleri anlamak kolay iki yöntem vardır. Yani benim ana kriterlerim son derece hızlı değil, çok okunabilir bir koda sahip olmak. Kodu anlaşılır tutmak için verilen olasılıkları tercih ederim:

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

"Var <= var2.keys ()" aşağıdaki testte daha hızlı yürütüyor olması, bunu tercih ederim.

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

Yalnızca bazı anahtarların eşleşip eşleşmediğinin belirlenmesi durumunda, bu işe yarar:

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

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

Sadece bazı tuşların eşleşip eşleşmediğini bulmak için başka bir seçenek:

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

if any_keys_i_seek & my_dict.keys():
    # code_here
    pass

0

Tüm tuşların bir dikte olup olmadığını tespit etmek için başka bir seçenek:

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

Bu işe yarıyor gibi görünüyor


Bu akıllıca ve kendim denemeden işe yaramadığına ikna oldum. Ben ()önce değerlendirilecek ve sonuç True, sonra kontrol eder şüpheli True in ok. Bu gerçekten nasıl çalışıyor ?!
durden2.0

7
('iki' ve 'bir' ve 'beş') 'beş' döndürür, bu yüzden aslında sadece 'beş'
dikte
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.