Überprüfen Sie, ob ein bestimmter Schlüssel bereits in einem Wörterbuch vorhanden ist, und erhöhen Sie ihn

294

Wie kann ich bei einem gegebenen Wörterbuch herausfinden, ob ein bestimmter Schlüssel in diesem Wörterbuch bereits auf einen Wert ungleich "Keine" gesetzt wurde?

Dh ich möchte das tun:

my_dict = {}

if (my_dict[key] != None):
  my_dict[key] = 1
else:
  my_dict[key] += 1

Das heißt, ich möchte den Wert erhöhen, wenn bereits einer vorhanden ist, oder ihn ansonsten auf 1 setzen.

Ben
quelle
11
Kleiner Code nitpick: Der Code setzt my_dict [key] auf 1, wenn bereits etwas vorhanden ist, und erhöht es, wenn es nicht vorhanden ist. Ich denke du willst ==, nicht! =.
QuantumFool

Antworten:

331

Sie suchen collections.defaultdict(verfügbar für Python 2.5+). Dies

from collections import defaultdict

my_dict = defaultdict(int)
my_dict[key] += 1

wird tun was du willst.

Für die regelmäßigen Python dicts, wenn es keinen Wert für einen bestimmten Schlüssel ist, werden Sie nicht erhalten , Nonewenn die dict Zugriff - eine KeyErrorwird erhöht werden. Wenn Sie also einen regulären dictCode anstelle Ihres Codes verwenden möchten, würden Sie diesen verwenden

if key in my_dict:
    my_dict[key] += 1
else:
    my_dict[key] = 1
dF.
quelle
8
Seinem Beispiel zufolge sollte es ausreichen, "defaultdict (lambda: 0)" zu setzen und die gesamte "if" -Klausel zu überspringen.
Deestan
Dies funktioniert, verwirrt jedoch Schlüssel und Werte (was das Lesen etwas seltsam macht). 'some_value' sollte 'some_key' sein
mikemaccana
@nailer: behoben, danke. Ich hatte ursprünglich 'some_value' verwendet, da dies der Variablenname in der Frage ist, aber ich stimme zu, dass es jetzt klarer ist.
dF.
20
... oder für normale dicts können Sie tun my_dict[key] = my_dict.get(key, 0) + 1.
Minmaxavg
Wie kann man dies auf verschachtelte Wörterbücher erweitern? diktiere [Schlüssel1] [Schlüssel2] + = 1?
Pablo Ruiz Ruiz
300

Ich bevorzuge dies in einer Codezeile.

my_dict = {}

my_dict [some_key] = my_dict.get (some_key, 0) + 1

Wörterbücher haben eine Funktion, get, die zwei Parameter akzeptiert - den gewünschten Schlüssel und einen Standardwert, falls dieser nicht vorhanden ist. Ich bevorzuge diese Methode gegenüber defaultdict, da Sie nur den Fall behandeln möchten, in dem der Schlüssel nicht in dieser einen Codezeile vorhanden ist, nicht überall.

Andrew Wilkinson
quelle
1
@ AndrewWilkinson mein schlechtes. Ich habe Ihre Antwort nicht so gründlich gelesen, wie ich es hätte tun sollen.
Maser
59

Ich persönlich benutze gerne setdefault()

my_dict = {}

my_dict.setdefault(some_key, 0)
my_dict[some_key] += 1
kichik
quelle
setdefaultist großartig. Der Wert wird nicht geändert, wenn bereits einer festgelegt ist some_key. Zum Beispiel d={1:2}; d.setdefault(1, 0)stört der Wert von nicht d[1].
Wsaleem
49

Dafür brauchen Sie die key in dictRedewendung.

if key in my_dict and not (my_dict[key] is None):
  # do something
else:
  # do something else

Sie sollten jedoch wahrscheinlich die Verwendung in Betracht ziehen defaultdict(wie von dF vorgeschlagen).

Eli Bendersky
quelle
1
Bitte beachten Sie, dass in mindestens 2.6 has_key () zugunsten von key in d beraubt wurde. Ich denke, dass es auch in 2.5 so war.
David Locke
Beachten Sie, dass man schreiben kann my_dict[key] is not None, was klarer ist (zumindest IMHO)
brandizzi
@ Brandizzi - zustimmen,if key in my_dict and my_dict[key]:
Rob Grant
18

Um die Frage zu beantworten, " wie kann ich herausfinden, ob ein bestimmter Index in diesem Diktat bereits auf einen Nicht-Keine-Wert gesetzt wurde ", würde ich Folgendes bevorzugen:

try:
  nonNone = my_dict[key] is not None
except KeyError:
  nonNone = False

Dies entspricht dem bereits angeführten Konzept der EAFP (leichter um Vergebung zu bitten als um Erlaubnis). Außerdem wird die Suche nach doppelten Schlüsseln im Wörterbuch vermieden, wie dies bei key in my_dict and my_dict[key] is not Noneinteressanten Suchvorgängen der Fall wäre.

Für das eigentliche Problem , das Sie gestellt haben, dh das Erhöhen eines int, falls vorhanden, oder das Setzen auf einen anderen Standardwert, empfehle ich auch das

my_dict[key] = my_dict.get(key, default) + 1

wie in der Antwort von Andrew Wilkinson.

Es gibt eine dritte Lösung, wenn Sie modifizierbare Objekte in Ihrem Wörterbuch speichern. Ein häufiges Beispiel hierfür ist eine Multimap , in der Sie eine Liste von Elementen für Ihre Schlüssel speichern. In diesem Fall können Sie Folgendes verwenden:

my_dict.setdefault(key, []).append(item)

Wenn im Wörterbuch kein Wert für key vorhanden ist, setzt ihn die setdefault-Methode auf den zweiten Parameter von setdefault. Es verhält sich wie ein Standard-my_dict [Schlüssel] und gibt den Wert für den Schlüssel zurück (der möglicherweise der neu eingestellte Wert ist).

nd.
quelle
Was in der Tat Pythonic (für einen Außenstehenden wie mich) aussieht, ist, dass jede Frage mindestens 3 gültige Antworten hat :)
Davka
@davka: Nun, die drei Anwendungsfälle sind fast gleich, aber unterschiedlich: a) Finden Sie heraus, ob das Wörterbuch ein Nicht-Keine-Element enthält. b) Rufen Sie einen Wert aus dem Wörterbuch ab oder verwenden Sie einen Standardwert, wenn der Wert nicht vorhanden ist c) Rufen Sie einen Wert aus dem Wörterbuch ab und speichern Sie einen Standardwert, wenn der Wert noch nicht vorhanden ist.
nd.
Ich weiß :) das ist keine Kritik, ich bin nur amüsiert von dieser Tatsache
Davka
In einem Kommentar zu @ ryeguys Antwort schlägt Stuart Woodward vor, "der Overhead bei der Ausnahmebehandlung in Sprachen ist immer um eine Größenordnung größer als die Suche nach Hash-Tabellen, die bestimmt, ob das Element im Wörterbuch vorhanden ist oder nicht", während Sie "Es" sagen vermeidet auch die Suche nach doppelten Schlüsseln im Wörterbuch ... wenn die Suche teuer ist "- hat jemand Messungen, bei denen die Ausnahmebehandlung schneller oder langsamer ist als eine Suche nach doppelten Schlüsseln?
Michael Firth
1
@MichaelFirth Ich habe flüchtig nach Pythons Ausnahme-Overhead gesucht : stackoverflow.com/questions/2522005/… es ist langsamer, aber nicht viel. Beachten Sie, dass das übergeordnete Konzept des Auslösens einer Ausnahme in verschiedenen Sprachen sehr unterschiedlich gehandhabt wird und Sie die Vor- und Nachteile nicht verallgemeinern können. Während die "Ausnahmen haben einen 10-fachen Overhead" für Java möglicherweise korrekt sind, gilt dies nicht für Python (oder Swift oder andere).
nd.
13

Einverstanden mit cgoldberg. Wie ich es mache ist:

try:
    dict[key] += 1
except KeyError:
    dict[key] = 1

Machen Sie es entweder wie oben beschrieben oder verwenden Sie ein Standarddiktat, wie andere vorgeschlagen haben. Verwenden Sie keine if-Anweisungen. Das ist nicht Pythonic.

Ryeguy
quelle
8
Wie sind wenn Aussagen nicht pythonisch?
Adam Parkin
2
Ich denke, dies ist ein Fall, in dem Pythons EAFP nicht der beste Weg ist. In Ihrem obigen Beispiel wurde der Code dupliziert. Was ist, wenn wir eines Tages wollen +=2oder -=1? Sie müssen daran denken, beide Zeilen zu ändern. Es mag jetzt wie eine triviale Sache erscheinen, aber das sind die Art von dummen kleinen 'trivialen' Fehlern, die zurückkommen können, um dich zu beißen.
Cam Jackson
3
Das sieht gut aus und funktioniert gut, aber ich vermeide es normalerweise so, weil ich dachte, dass der Overhead bei der Ausnahmebehandlung in Sprachen immer um eine Größenordnung größer ist als die Suche nach Hash-Tabellen, die bestimmt, ob das Element im Wörterbuch vorhanden ist oder nicht.
Stuart Woodward
11

Wie Sie den vielen Antworten entnehmen können, gibt es mehrere Lösungen. Eine Instanz von LBYL (schauen, bevor Sie springen) wurde noch nicht erwähnt, die Methode has_key ():

my_dict = {}

def add (key):
    if my_dict.has_key(key):
        my_dict[key] += 1
    else:
        my_dict[key] = 1

if __name__ == '__main__':
    add("foo")
    add("bar")
    add("foo")
    print my_dict
bortzmeyer
quelle
6
has_key () ist langsamer als der Operator 'in' und weniger lesbar.
Abgan
9
... und es wurde in Python 2.6 veraltet und in Python 3 entfernt.
Tim Pietzcker
7

Die Art und Weise, wie Sie dies versuchen, heißt LBYL (schauen Sie, bevor Sie springen), da Sie die Bedingungen überprüfen, bevor Sie versuchen, Ihren Wert zu erhöhen.

Der andere Ansatz heißt EAFP (leichter um Vergebung zu bitten als um Erlaubnis). In diesem Fall würden Sie einfach die Operation versuchen (den Wert erhöhen). Wenn dies fehlschlägt, fangen Sie die Ausnahme ab und setzen den Wert auf 1. Dies ist eine etwas pythonischere Methode (IMO).

http://mail.python.org/pipermail/python-list/2003-May/205182.html

Corey Goldberg
quelle
5

Ein bisschen spät, aber das sollte funktionieren.

my_dict = {}
my_dict[key] = my_dict[key] + 1 if key in my_dict else 1
Bob
quelle
Wow, als Java-Programmierer ist dies ein ziemlich verrückt aussehendes Konstrukt. Es sieht aus wie ein seltsam geordneter ternärer Operator?
Forresthopkinsa
5

Dies beantwortet die Frage nicht direkt, aber für mich sieht es so aus, als ob Sie die Funktionalität von Sammlungen möchten .

from collections import Counter

to_count = ["foo", "foo", "bar", "baz", "foo", "bar"]

count = Counter(to_count)

print(count)

print("acts just like the desired dictionary:")
print("bar occurs {} times".format(count["bar"]))

print("any item that does not occur in the list is set to 0:")
print("dog occurs {} times".format(count["dog"]))

print("can iterate over items from most frequent to least:")
for item, times in count.most_common():
    print("{} occurs {} times".format(item, times))

Dies führt zur Ausgabe

Counter({'foo': 3, 'bar': 2, 'baz': 1})
acts just like the desired dictionary:
bar occurs 2 times
any item that does not occur in the list is set to 0:
dog occurs 0 times
can iterate over items from most frequent to least:
foo occurs 3 times
bar occurs 2 times
baz occurs 1 times
Izaak van Dongen
quelle
Der Zähler funktioniert genau wie defaultdict(int)einige zusätzliche Funktionen, sodass er perfekt funktioniert, wenn ausschließlich mit Ganzzahlen gearbeitet wird, Sie jedoch keines der relevanten Verhaltensweisen anzeigen.
Tadhg McDonald-Jensen
4

Hier ist ein Einzeiler, den ich kürzlich zur Lösung dieses Problems entwickelt habe. Es basiert auf der setdefault- Wörterbuchmethode:

my_dict = {}
my_dict[key] = my_dict.setdefault(key, 0) + 1
Igor Gai
quelle
0

Ich habe danach gesucht, es nicht im Web gefunden, dann mein Glück mit Try / Error versucht und es gefunden

my_dict = {}

if my_dict.__contains__(some_key):
  my_dict[some_key] += 1
else:
  my_dict[some_key] = 1
AbhishekKr
quelle
1
Sie sollten nicht __contains__in einem Produktionscode verwenden. Übrigens. __contains__ist das gleiche wie mit is.
user1767754
1
my_dict.__contains__(some_key)ist gleichbedeutend mit some_key in my_dict, ist Überlastung für inBetreiber nichtis
Tadhg McDonald-Jensen