Sprawdź, czy dany klucz już istnieje w słowniku i zwiększ go


294

Biorąc pod uwagę słownik, jak mogę się dowiedzieć, czy dany klucz w tym słowniku został już ustawiony na wartość inną niż Brak?

Tzn. Chcę to zrobić:

my_dict = {}

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

To znaczy, chcę zwiększyć wartość, jeśli już tam jest, lub ustawić ją na 1 w przeciwnym razie.


11
Mały kod nitpick: kod ustawia my_dict [klucz] na 1, jeśli już coś tam jest, i zwiększa go, jeśli nie ma. Myślę, że chcesz ==, nie! =.
QuantumFool,

Odpowiedzi:


331

Szukasz collections.defaultdict(dostępne dla Python 2.5+). To

from collections import defaultdict

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

zrobi co chcesz.

W przypadku zwykłych Pythonów dict, jeśli nie ma wartości dla danego klucza, nie dostaniesz się Nonepodczas uzyskiwania dostępu do słownika - KeyErrorzostanie podniesione. Więc jeśli chcesz używać zwykłego dict, zamiast kodu, którego byś użył

if key in my_dict:
    my_dict[key] += 1
else:
    my_dict[key] = 1

8
Zgodnie z jego przykładem powinno wystarczyć ustawienie „defaultdict (lambda: 0)” i pominięcie całej klauzuli „if”.
Deestan

Działa to, ale myli klucze i wartości (co sprawia, że ​​czytanie jest nieco dziwne). „some_value” powinno być „some_key”
mikemaccana

@nailer: naprawiono, dziękuję. Początkowo użyłem „some_value”, ponieważ jest to nazwa zmiennej w pytaniu, ale zgadzam się, że teraz jest jaśniejsza.
dF.

20
... lub dla zwykłych dict, możesz to zrobić my_dict[key] = my_dict.get(key, 0) + 1.
minmaxavg

Jak rozszerzyć to na zagnieżdżone słowniki? dyktować [klucz1] [klucz2] + = 1?
Pablo Ruiz Ruiz

300

Wolę to zrobić w jednym wierszu kodu.

my_dict = {}

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

Słowniki mają funkcję get, która pobiera dwa parametry - żądany klucz i wartość domyślną, jeśli nie istnieje. Wolę tę metodę niż defaultdict, ponieważ chcesz obsłużyć przypadek, w którym klucz nie istnieje w tym jednym wierszu kodu, nie wszędzie.


1
@AndrewWilkinson my bad. Nie przeczytałem twojej odpowiedzi tak dokładnie, jak powinienem.
masaers

59

Osobiście lubię używać setdefault()

my_dict = {}

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

setdefaultjest niesamowite. Nie zmienia wartości, jeśli jest już ustawiona some_key. Na przykład d={1:2}; d.setdefault(1, 0)nie zaburza wartości d[1].
wsaleem,

49

Potrzebujesz do tego key in dictidiomu.

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

Jednak prawdopodobnie powinieneś rozważyć użycie defaultdict(jak sugeruje dF).


1
Należy pamiętać, że co najmniej 2.6 has_key () zostało obniżone na korzyść klucza id. Myślę, że tak było również w wersji 2.5.
David Locke

Zauważ, że można pisać my_dict[key] is not None, co jest jaśniejsze (przynajmniej IMHO)
brandizzi

@brandizzi - zgadzam się,if key in my_dict and my_dict[key]:
Rob Grant,

18

Aby odpowiedzieć na pytanie „ jak mogę się dowiedzieć, czy dany indeks w tym dykcie został już ustawiony na wartość inną niż Brak ”, wolałbym:

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

Jest to zgodne z już przywołaną koncepcją EAFP (łatwiej prosić o wybaczenie niż o pozwolenie). Unika także podwójnego wyszukiwania klucza w słowniku, tak jak by to było w przypadkukey in my_dict and my_dict[key] is not None interesujące, gdyby wyszukiwanie było drogie.

W przypadku rzeczywistego problemu , który wystąpiłeś, tj. Zwiększenie wartości int, jeśli istnieje, lub ustawienie jej wartości domyślnej w innym przypadku, również polecam

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

jak w odpowiedzi Andrew Wilkinsona.

Istnieje trzecie rozwiązanie, jeśli przechowujesz modyfikowalne obiekty w słowniku. Typowym tego przykładem jest multimapa , w której przechowujesz listę elementów dla swoich kluczy. W takim przypadku możesz użyć:

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

Jeśli wartość klucza nie istnieje w słowniku, metoda setdefault ustawi drugi parametr setdefault. Zachowuje się jak standardowy my_dict [klucz], zwracając wartość klucza (która może być wartością nowo ustawioną).


wygląda rzeczywiście pythonic (na zewnątrz jak ja) jest to, że każde pytanie ma przynajmniej 3 prawidłowych odpowiedzi :)
Davka Corporation

@davka: Cóż, trzy przypadki użycia są prawie takie same, ale różne: a) dowiedz się, czy w słowniku jest element inny niż Brak b) pobierz wartość ze słownika lub użyj wartości domyślnej, jeśli wartość nie istnieje c) pobrać wartość ze słownika i zapisać wartość domyślną, jeśli wartość jeszcze nie istnieje.
nd.

Wiem :) to nie jest krytyka, po prostu mnie to bawi
davka,

W komentarzu do odpowiedzi @ ryeguy Stuart Woodward sugeruje, że „koszty ogólne związane z obsługą wyjątków w językach są zawsze o rząd wielkości większe niż wyszukiwanie w tablicy skrótów, która określa, czy element istnieje w słowniku”, podczas gdy mówisz „To” unika także podwójnego wyszukiwania klucza w słowniku ... jeśli wyszukiwanie jest drogie "- czy ktoś ma jakieś pomiary, gdzie obsługa wyjątków jest szybsza lub wolniejsza niż wyszukiwanie podwójnym kluczem?
Michael Firth,

1
@MichaelFirth Przeszukałem pobieżnie informacje dotyczące wyjątku Pythona: stackoverflow.com/questions/2522005/... jest wolniejszy, ale nie za dużo. Należy pamiętać, że koncepcja wysokiego poziomu zgłaszania wyjątku jest obsługiwana bardzo różnie w różnych językach i nie można uogólniać zalet i wad. Więc chociaż „wyjątki mają 10-krotny narzut” mogą być poprawne dla Javy, nie dotyczy to Pythona (ani Swift, ani innych).
nd.

13

Uzgodniony z Cgoldbergiem. Jak to robię to:

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

Więc albo zrób to jak wyżej lub użyj domyślnego dykta, jak sugerowali inni. Nie używaj instrukcji if. To nie jest Python.


8
Jak się dzieje, jeśli instrukcje nie są Pythonic?
Adam Parkin,

2
Myślę, że to jest jeden przypadek, w którym EAFP Pythona nie jest najlepszym sposobem. Powyższy przykład zawiera zduplikowany kod; co jeśli pewnego dnia chcemy +=2lub -=1? Musisz pamiętać o zmianie obu linii. To może wydawać się teraz trywialne, ale są to takie głupie małe „trywialne” błędy, które mogą wrócić i cię ugryźć.
Cam Jackson

3
Wygląda to ładnie i działa dobrze, ale zwykle unikam takiego postępowania, ponieważ myślałem, że narzut w obsłudze wyjątków w językach jest zawsze o rząd wielkości większy niż wyszukiwanie w tablicy skrótów, która określa, czy element istnieje w słowniku.
Stuart Woodward,

11

Jak widać z wielu odpowiedzi, istnieje kilka rozwiązań. O jednej instancji LBYL (patrz przed skokiem) jeszcze nie wspomniano, metoda 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

6
has_key () jest wolniejszy niż operator „in” i jest mniej czytelny.
Abgan

9
... i został przestarzały w Pythonie 2.6 i usunięty w Pythonie 3.
Tim Pietzcker

7

Sposób, w jaki próbujesz to zrobić, nazywa się LBYL (spójrz przed skokiem), ponieważ sprawdzasz warunki, zanim spróbujesz zwiększyć swoją wartość.

Inne podejście nazywa się EAFP (łatwiej prosić o wybaczenie niż o pozwolenie). W takim przypadku po prostu spróbuj wykonać operację (zwiększ wartość). Jeśli się nie powiedzie, wychwytujesz wyjątek i ustawiasz wartość na 1. Jest to nieco bardziej Pythoniczny sposób na zrobienie tego (IMO).

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


5

Trochę późno, ale to powinno zadziałać.

my_dict = {}
my_dict[key] = my_dict[key] + 1 if key in my_dict else 1

Wow, jako programista Java, jest to konstrukcja dość szalona. Wygląda jak dziwnie uporządkowany operator trójskładnikowy?
forresthopkinsa

5

To nie jest bezpośrednia odpowiedź na pytanie, ale dla mnie wygląda na to, że możesz chcieć funkcjonalności kolekcji .

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

Daje to wynik

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

Licznik działa tak samo, jak defaultdict(int)z pewną dodatkową funkcjonalnością, więc działałby doskonale, gdy miałby do czynienia wyłącznie z liczbami całkowitymi, ale nie pokazujesz żadnego z odpowiednich zachowań.
Tadhg McDonald-Jensen

4

Oto jeden linijka, którą ostatnio wymyśliłem, aby rozwiązać ten problem. Opiera się na metodzie słownika setdefault :

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

0

Szukałem tego, nie znalazłem go w Internecie, a potem spróbowałem szczęścia z Try / Error i znalazłem

my_dict = {}

if my_dict.__contains__(some_key):
  my_dict[some_key] += 1
else:
  my_dict[some_key] = 1

1
Nie powinieneś używać __contains__w kodzie produkcyjnym. btw. __contains__jest taki sam jak przy użyciu is.
user1767754

1
my_dict.__contains__(some_key)jest równoważne some_key in my_dict, jest przeciążeniem dla inoperatora nieis
Tadhg McDonald-Jensen
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.