Wie überprüfe ich in einem Durchgang, ob mehrere Schlüssel in einem Diktat enthalten sind?

217

Ich möchte so etwas tun wie:

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

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

Wie überprüfe ich, ob sowohl 'foo' als auch 'bar' in dict foo sind?

Jean-François Corbett
quelle

Antworten:

362

Nun, Sie könnten dies tun:

>>> if all (k in foo for k in ("foo","bar")):
...     print "They're there!"
...
They're there!
hughdbrown
quelle
10
+1, das gefällt mir besser als Gregs Antwort, weil es prägnanter UND schneller ist (kein Aufbau einer irrelevanten temporären Liste UND vollständige Ausnutzung von Kurzschlüssen).
Alex Martelli
4
Ich liebe alle () und alle (). Sie machen so viele Algorithmen so viel sauberer.
Hughdbrown
Letztendlich habe ich diese Lösung verwendet. Es schien das Beste für größere Datensätze. Bei der Suche nach beispielsweise 25 oder 30 Tasten.
4
Dank Kurzschluss ist dies eine gute Lösung, insbesondere wenn der Test häufig fehlschlägt. es sei denn, Sie können den interessierenden Schlüsselsatz nur einmal erstellen und mehrmals überprüfen. In diesem Fall setist er überlegen. Wie immer ... messen Sie es! -)
Alex Martelli
Ich benutze dies immer dann, wenn es schöner aussieht als der "normale" Weg, mit all dem und oder dem oder ... es ist auch schön, weil du entweder "alle" oder "alle" verwenden kannst ... zusätzlich kannst du entweder haben " k in foo "oder" k nicht in foo "je nach dem Test, den Sie durchführen
möchten
123
if {"foo", "bar"} <= myDict.keys(): ...

Wenn Sie noch mit Python 2 arbeiten, können Sie dies tun

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

Wenn Sie noch auf einem wirklich alten Python <= 2.6 sind, können Sie setdas Diktat aufrufen , aber es wird das gesamte Diktat durchlaufen, um das Set zu erstellen, und das ist langsam:

if set(("foo", "bar")) <= set(myDict): ...
Alex Martelli
quelle
sieht gut aus! Das einzige, was ich nicht mag, ist, dass Sie temporäre Sets erstellen müssen, aber es ist sehr kompakt. Also muss ich sagen ... nette Verwendung von Sets!
Terence Honles
17
In Python 3 können Sie sagen, set(("foo","bar")) <= myDict.keys()was das temporäre Set vermeidet und somit viel schneller ist. Für meine Tests ist es ungefähr die gleiche Geschwindigkeit wie für alle, wenn die Abfrage 10 Elemente umfasste. Es wird jedoch langsamer, wenn die Abfrage größer wird.
John La Rooy
1
Ich habe einige meiner Tests als Antwort veröffentlicht. stackoverflow.com/questions/1285911/…
John La Rooy
30
if {'foo', 'bar'} <= set(myDict): ...
Boris Raicheff
11
Für alle, die sich fragen, warum dies funktioniert: Der Operator <= entspricht der Methode .set issubset (): docs.python.org/3/library/stdtypes.html#set-types-set-frozenset
edepe
41

Einfaches Benchmarking-Rig für 3 der Alternativen.

Geben Sie Ihre eigenen Werte für D und Q ein


>>> 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
quelle
4
Python 2.7 muss d.viewkeys()machen set(q) <= d.viewkeys().
Martijn Pieters
Python 2.7.5hat auch d.keys()Methode.
Ivan Kharlamov
3
@IvanKharlamov, aber in Python2 gibt es kein Objekt zurück, das mit kompatibel istset(q) <= ...
John La Rooy
1
Mein schlechtes, du bist absolut genau richtig: es kehrt zurück TypeError: can only compare to a set. Es tut uns leid! :))
Ivan Kharlamov
1
Für Python 2 wechseln Sie die Reihenfolge : d.viewkeys() >= set(q). Ich bin hierher gekommen, um herauszufinden, warum die Bestellung wichtig ist!
Veedrac
34

Sie müssen die linke Seite nicht in ein Set einwickeln. Sie können dies einfach tun:

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

Dies ist auch besser als die all(k in d...)Lösung.

Claytonk
quelle
2
Dies ist auch besser als die all-Lösung (k in d ...). Ich schlug dies als Bearbeitung vor, aber es wurde abgelehnt, weil es besser war, einen Kommentar hinzuzufügen . Also hier mache ich genau das
miraculixx
@miraculixx Es ist nicht besser, einen Kommentar hinzuzufügen. Es ist besser, relevante Informationen in einer Antwort zu bearbeiten und die Kommentare zu löschen.
Endolith
1
@endolith Ich stimme zu, einige Leute tun dies offensichtlich nicht, wie Sie in der abgelehnten Bearbeitung sehen können, die ich an erster Stelle vorgenommen habe. Jedenfalls ist das eine Diskussion für Meta, nicht für hier.
Miraculixx
Kann das bitte jemand erklären? Ich habe festgestellt, dass {} eine Menge erstellt, aber wie funktioniert der Operator "weniger als oder gleich" hier?
Locane
1
@Locane Der Operator <= testet, ob die erste Menge eine Teilmenge der zweiten Menge ist. Sie können auch {'foo', 'bar'}. Issubset (somedict) ausführen. Die Dokumentation für die Set-Methodik finden Sie hier: docs.python.org/2/library/sets.html
Meow
24

Verwenden von Sets :

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

Alternative:

if set(("foo", "bar")) <= set(foo):
    #do stuff
Karl Voigtland
quelle
2
set (d), wie ich es in meiner Antwort verwendet habe, ist genau wie set (d.keys ()), aber schneller, kürzer und ich würde sagen, stilistisch vorzuziehen.
Alex Martelli
set(d)ist das gleiche wie set(d.keys())(ohne die Zwischenliste, die d.keys()konstruiert)
Jochen Ritzel
11

Wie wäre es damit:

if all([key in foo for key in ["foo","bar"]]):
    # do stuff
    pass
Greg
quelle
8
in der Tat nicht nur unnötig, sondern auch positiv schädlich, da sie das normale Kurzschlussverhalten von behindern all.
Alex Martelli
10

Ich denke, das ist das klügste und markigste.

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

Obwohl ich Alex Martellis Antwort mag, scheint sie mir nicht pythonisch zu sein. Das heißt, ich dachte, ein wichtiger Teil des Pythonischen ist es, leicht verständlich zu sein. Mit diesem Ziel <=ist nicht leicht zu verstehen.

Während es mehr Zeichen sind, issubset()ist es verständlicher, die von Karl Voigtland vorgeschlagene Antwort zu verwenden. Da diese Methode ein Wörterbuch als Argument verwenden kann, lautet eine kurze, verständliche Lösung:

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

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

Ich würde gerne {'foo', 'bar'}anstelle von verwenden set(('foo', 'bar')), weil es kürzer ist. Es ist jedoch nicht so verständlich und ich denke, die Klammern sind zu leicht als Wörterbuch zu verwechseln.

LS
quelle
2
Ich denke, es ist verständlich, wenn Sie verstehen, was es bedeutet.
Bobort
Es ist in der Dokumentation als Synonym für .issubset(). Ich denke, in der Python-Dokumentation zu sein, macht es standardmäßig zu Pythonic.
Ingyhere
4

Die Lösung von Alex Martelli set(queries) <= set(my_dict)ist der kürzeste Code, aber möglicherweise nicht der schnellste. Angenommen, Q = len (Abfragen) und D = len (my_dict).

Dies erfordert O (Q) + O (D), um die beiden Mengen zu erstellen, und dann (man hofft!) Nur O (min (Q, D)), um den Teilmengen-Test durchzuführen - vorausgesetzt natürlich, dass Python-Set-Lookup ist O (1) - dies ist der schlimmste Fall (wenn die Antwort wahr ist).

Die Generatorlösung von hughdbrown (et al?) Ist all(k in my_dict for k in queries)O (Q) im ungünstigsten Fall.

Komplizierende Faktoren:
(1) Die Schleifen im satzbasierten Gadget werden alle mit C-Geschwindigkeit ausgeführt, während das beliebige Gadget den Bytecode durchläuft.
(2) Der Aufrufer des beliebigen Gadgets kann möglicherweise jede Kenntnis der Wahrscheinlichkeit eines Fehlers nutzen, um die Abfrageelemente entsprechend zu ordnen, während das satzbasierte Gadget keine solche Steuerung zulässt.

Wenn Geschwindigkeit wichtig ist, ist wie immer ein Benchmarking unter Betriebsbedingungen eine gute Idee.

John Machin
quelle
1
Der Generator war in allen Fällen, die ich ausprobiert habe, schneller. stackoverflow.com/questions/1285911/…
John La Rooy
2

Sie können mit .issubset () als auch

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

Wie wäre es mit Lambda?

 if reduce( (lambda x, y: x and foo.has_key(y) ), [ True, "foo", "bar"] ): # do stuff
Jinuk Kim
quelle
2
Diese Antwort ist die einzige funktional korrekte, die mit einer einfachen Änderung (s / True / 1 /) auf Python 1.5 funktioniert ... aber es gibt nichts anderes. UND das True-Ding wäre besser als das optionale Initialisierungsarg, als sich in die Vorderseite des Sequenzarg zu drängen.
John Machin
1

Falls Sie möchten:

  • Holen Sie sich auch die Werte für die Schlüssel
  • Überprüfen Sie mehr als ein Diktat

dann:

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

Um nicht zu behaupten, dass dies nicht etwas ist, an das Sie nicht gedacht haben, aber ich finde, dass das Einfachste normalerweise das Beste ist:

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

Jason, () sind in Python nicht notwendig.

Juanjo Conti
quelle
3
Trotzdem könnten sie einen guten Stil haben ... ohne sie fragt sich mein C ++ - verwirrtes Gehirn immer, ob es als "wenn 'foo in (foo und' bar ') in foo interpretiert werden soll:"
Jeremy Friesner
1
Ich verstehe, dass sie nicht notwendig sind. Ich habe nur das Gefühl, dass sie in diesem Fall Klarheit schaffen.
Jason Baker
0

Nur meine Meinung dazu, es gibt zwei Methoden, die für alle gegebenen Optionen leicht zu verstehen sind. Mein Hauptkriterium ist also, gut lesbaren Code zu haben, nicht außergewöhnlich schnellen Code. Um den Code verständlich zu halten, bevorzuge ich gegebene Möglichkeiten:

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

Die Tatsache, dass "var <= var2.keys ()" in meinen Tests unten schneller ausgeführt wird, bevorzuge ich diesen.

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

Wenn festgestellt wird, ob nur einige Schlüssel übereinstimmen, funktioniert dies:

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

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

Eine weitere Option, um festzustellen, ob nur einige Schlüssel übereinstimmen:

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

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

Eine weitere Option, um festzustellen, ob sich alle Schlüssel in einem Diktat befinden:

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
ingyhere
quelle
-4
>>> ok
{'five': '5', 'two': '2', 'one': '1'}

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

Das scheint zu funktionieren

Prashanth Gowda
quelle
Das ist klug und ich war überzeugt, dass es nicht funktioniert hat, bis ich es selbst ausprobiert habe. Ich vermutete (), dass das zuerst ausgewertet werden und dazu führen Truewürde, was dann prüfen würde, ob True in ok. Wie funktioniert das eigentlich?!
Durden2.0
7
('zwei' und 'eins' und 'fünf') gibt 'fünf' zurück, so dass nur geprüft wird, ob 'fünf' auf dem
Diktat steht