328

How do I find out if a key in a dictionary has already been set to a non-None value?

I want to increment the value if there's already one there, or set it to 1 otherwise:

my_dict = {}

if my_dict[key] is not None:
  my_dict[key] = 1
else:
  my_dict[key] += 1
Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
Ben
  • 66,838
  • 37
  • 84
  • 108
  • 12
    Small code nitpick: the code sets my_dict[key] to 1 if there's already something there, and increments it if there isn't. I think you want ==, not !=. – QuantumFool Jun 23 '16 at 17:20

12 Answers12

354

You are looking for collections.defaultdict (available for Python 2.5+). This

from collections import defaultdict

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

will do what you want.

For regular Python dicts, if there is no value for a given key, you will not get None when accessing the dict -- a KeyError will be raised. So if you want to use a regular dict, instead of your code you would use

if key in my_dict:
    my_dict[key] += 1
else:
    my_dict[key] = 1
JamesThomasMoon
  • 6,169
  • 7
  • 37
  • 63
dF.
  • 74,139
  • 30
  • 130
  • 136
350

I prefer to do this in one line of code.

my_dict = {}

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

Dictionaries have a function, get, which takes two parameters - the key you want, and a default value if it doesn't exist. I prefer this method to defaultdict as you only want to handle the case where the key doesn't exist in this one line of code, not everywhere.

kba
  • 19,333
  • 5
  • 62
  • 89
Andrew Wilkinson
  • 10,682
  • 3
  • 35
  • 38
  • 2
    I prefer this solution to the selected answer because it does not require installing another dependency. – Erol Dec 30 '20 at 09:24
  • 2
    @Erol `defaultdict` is [part of the Python standard library](https://docs.python.org/3/library/collections.html). So no installing necessary! – François Leblanc Apr 14 '21 at 00:06
64

I personally like using setdefault()

my_dict = {}

my_dict.setdefault(some_key, 0)
my_dict[some_key] += 1
kichik
  • 33,220
  • 7
  • 94
  • 114
  • 1
    `setdefault` is awesome. It does not alter the value if one is already set for `some_key`. For example, `d={1:2}; d.setdefault(1, 0)` does not disturb the value of `d[1]`. – wsaleem Oct 19 '19 at 22:33
  • The other answers wouldn't work but this answer did for `self.site_included.setdefault(i, [])` followed by `self.site_included[i].append(self.post_index)`. – WinEunuuchs2Unix Jan 28 '22 at 01:45
50

You need the key in dict idiom for that.

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

However, you should probably consider using defaultdict (as dF suggested).

Eli Bendersky
  • 263,248
  • 89
  • 350
  • 412
21

To answer the question "how can I find out if a given index in that dict has already been set to a non-None value", I would prefer this:

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

This conforms to the already invoked concept of EAFP (easier to ask forgiveness then permission). It also avoids the duplicate key lookup in the dictionary as it would in key in my_dict and my_dict[key] is not None what is interesting if lookup is expensive.

For the actual problem that you have posed, i.e. incrementing an int if it exists, or setting it to a default value otherwise, I also recommend the

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

as in the answer of Andrew Wilkinson.

There is a third solution if you are storing modifyable objects in your dictionary. A common example for this is a multimap, where you store a list of elements for your keys. In that case, you can use:

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

If a value for key does not exist in the dictionary, the setdefault method will set it to the second parameter of setdefault. It behaves just like a standard my_dict[key], returning the value for the key (which may be the newly set value).

nd.
  • 8,699
  • 2
  • 32
  • 42
  • what looks indeed Pythonic (to an outsider like myself) is that any question has at least 3 valid answers :) – davka Apr 12 '11 at 16:40
  • @davka: Well, the three use cases are almost the same, but different: a) find out if there is a non-None element in the dictionary b) retrieve a value from the dictionary or use a default if the value does not exist c) retrieve a value from the dictionary and store a default if the value does not exist yet. – nd. Apr 13 '11 at 15:19
  • I know :) this is not a critique, I am just amused by this fact – davka Apr 13 '11 at 16:57
  • In a comment to @ryeguy's answer, Stuart Woodward suggests "the overhead in Exception handling in languages is always a order of magnitude greater than the hash table lookup that determines whether the item exists or not in the dictionary", while you are saying "It also avoids the duplicate key lookup in the dictionary ... if lookup is expensive" - does anyone have any measurements of where the exception handling is faster or slower than a double key lookup? – Michael Firth Apr 17 '19 at 09:08
  • 1
    @MichaelFirth I did a cursory search for Python's exception overhead: https://stackoverflow.com/questions/2522005/cost-of-exception-handlers-in-python it's slower, but not by a lot. Keep in mind that the high level concept of *throwing an Exception* is handled very differently in different languages and you cannot generalize the pros and cons. So while the "Exceptions have a 10x overhead" might be correct for Java, it is not for Python (or Swift or others). – nd. Apr 25 '19 at 09:21
14

Agreed with cgoldberg. How I do it is:

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

So either do it as above, or use a default dict as others have suggested. Don't use if statements. That's not Pythonic.

ryeguy
  • 65,519
  • 58
  • 198
  • 260
  • 8
    How are if statements not Pythonic? – Adam Parkin Nov 08 '11 at 21:20
  • 2
    I think this is one case where Python's EAFP is not the best way. Your example above has duplicated code; what if one day we want `+=2` or `-=1`? You have to remember to change both lines. It might seem like a trivial thing now, but those are the kind of stupid little 'trivial' bugs that can come back to bite you. – Cam Jackson Jan 04 '12 at 02:33
  • 3
    This looks nice and works fine, but I usually avoid doing it like this because I thought that the overhead in Exception handling in languages is always a order of magnitude greater than the hash table lookup that determines whether the item exists or not in the dictionary. – Stuart Woodward Jul 29 '12 at 02:37
11

As you can see from the many answers, there are several solutions. One instance of LBYL (look before you leap) has not been mentioned yet, the has_key() method:

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
  • 34,164
  • 12
  • 67
  • 91
8

A bit late but this should work.

my_dict = {}
my_dict[key] = my_dict[key] + 1 if key in my_dict else 1
Bob
  • 849
  • 5
  • 14
  • 26
8

The way you are trying to do it is called LBYL (look before you leap), since you are checking conditions before trying to increment your value.

The other approach is called EAFP (easier to ask forgiveness then permission). In that case, you would just try the operation (increment the value). If it fails, you catch the exception and set the value to 1. This is a slightly more Pythonic way to do it (IMO).

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

Corey Goldberg
  • 59,062
  • 28
  • 129
  • 143
5

This isn't directly answering the question, but to me, it looks like you might want the functionality of collections.Counter.

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

This results in the output

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
  • 2,450
  • 13
  • 23
  • Counter works just like `defaultdict(int)` with some extra functionality so it would work perfectly when dealing exclusively with integers but you don't show any of the relevant behaviours. – Tadhg McDonald-Jensen Nov 01 '17 at 12:08
3

Here's one-liner that I came up with recently for solving this problem. It's based on the setdefault dictionary method:

my_dict = {}
my_dict[key] = my_dict.setdefault(key, 0) + 1
Igor Gai
  • 295
  • 3
  • 8
-2

I was looking for it, didn't found it on web then tried my luck with Try/Error and found it

my_dict = {}

if my_dict.__contains__(some_key):
  my_dict[some_key] += 1
else:
  my_dict[some_key] = 1
kba
  • 19,333
  • 5
  • 62
  • 89
AbhishekKr
  • 300
  • 2
  • 12