Dictionaries and incrementing keys

S

Steve Crook

Hi all,

I've always done key creation/incrementation using:

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

Today I spotted an alternative:

dict[key] = dict.get(key, 0) + 1

Whilst certainly more compact, I'd be interested in views on how
pythonesque this method is.
 
P

Peter Otten

A

AlienBaby

Steve said:
I've always done key creation/incrementation using:
if key in dict:
    dict[key] += 1
else:
    dict[key] = 1

Your way is usually faster than
dict[key] = dict.get(key, 0) + 1
Whilst certainly more compact, I'd be interested in views on how
pythonesque this method is.

You may also consider

http://docs.python.org/library/coll.../library/collections.html#collections.Counter



How do those methods compare to the one I normally use;

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

Steve Crook

How do those methods compare to the one I normally use;

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

This is a lot slower in percentage terms. You should also qualify the
exception: except KeyError
 
S

Steven D'Aprano

Hi all,

I've always done key creation/incrementation using:

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

Today I spotted an alternative:

dict[key] = dict.get(key, 0) + 1

Whilst certainly more compact, I'd be interested in views on how
pythonesque this method is.

Either version is perfectly fine. There's no reason to avoid either other
than personal preference.

The "if key in dict" version does up to three item lookups (first to see
if the key is in the dict, then to fetch the value, then to assign it),
the version with dict.get only does two.

If the key has an expensive hash function, the version using dict.get
will be much faster:
.... d[key] += 1
.... else:
.... d[key] = 1
.... """, "from __main__ import key; d = {key: 0}")
t2 = Timer("d[key] = d.get(key, 0) + 1", .... "from __main__ import key; d = {key: 0}")

min(t1.repeat()) 8.739075899124146
min(t2.repeat())
6.425030946731567

but that will rarely be a problem in practice. For "normal" keys which
are small strings or ints, the "if key in dict" version will usually be
faster. Unless there are lots of missing keys, in which case the version
using dict.get may be faster.

Either way, the difference is unlikely to be significant except for the
tightest of tight loops.
 
S

Steven D'Aprano

How do those methods compare to the one I normally use;

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

This is a lot slower in percentage terms. You should also qualify the
exception: except KeyError

Not necessarily. It depends on how often you have KeyError.

By my measurements, if the key is usually present, it is faster to use
try...except. Only if the key is frequently missing does it become faster
to test first.
 
A

Asen Bozhilov

Steve said:
Whilst certainly more compact, I'd be interested in views on how
pythonesque this method is.

Instead of calling function you could use:

d = {}

d[key] = (key in d and d[key]) + 1

Regards.
 
R

Raymond Hettinger

Today I spotted an alternative:

dict[key] = dict.get(key, 0) + 1

Whilst certainly more compact, I'd be interested in views on how
pythonesque this method is.

It is very pythonesque in the it was the traditional one way to do it
(also one of the fastest ways).

Now we have collections.Counter which simplifies the code to:

c = Counter()
...
c[key] += 1

For existing keys, it is as fast as a regular dictionary (because it
is a dict subclass and it does not override or extend either
__getitem__ or __setitem__). For new keys, it is a little slower
because it calls the __missing__ method which returns zero.

Raymond
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,767
Messages
2,569,572
Members
45,046
Latest member
Gavizuho

Latest Threads

Top