Dictionaries

Dictionaries are also known as associative memories or associative arrays. They are indexed by keys that can be any immutable type, so strings and numbers can always be keys as well as tuples (if they contain only strings, numbers, or tuples). In fact, if a tuple contains any mutable object either directly or indirectly, it cannot be used as a key [1].

>>> d = {
...     "a": 1,
...     "b": 2,
... }

>>> "a" in d
True

>>> "c" in d
False

>>> d["e"] + 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'e'
>>> d.get("e", 0) + 2  # use `get` to avoid `KeyError` and
                       # to use a default value for missing keys

>>> d["c"] = 3
>>> "c" in d
True

>>> d[[1, 2]] = 2  # cannot hash a `list` since it is *mutable*
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

>>> d[(1, 2)] = 2  # a `tuple` is *immutable*, hence hashable

Operations

Let's check the complexity of dictionaries' common operations [2]:

OperationTime ComplexitySpace ComplexitySyntax
Sizelen(d)
Get itemd[k] or d.get(k)
Set itemd[k] = v
Delete itemdel d[k]
Key in dictk in d
Iteratefor k, v in d.items(): print(k, v)

Best Practices

Use items()

# Good
for k, v in d.items():
    print(f"{k} -> {v}")

# Not-so-good
for k in d:
    print(f"{k} -> {d[k]}")

Use collections.defaultdict

# Good
from collections import defaultdict

d = defaultdict(int)
d["a"] += 1  # `defaultdict(<class 'int'>, {'a': 1})`
# ~

# Slightly better
d = {}
d["a"] = d.get("a", 0) + 1

# Not-so-good
d = {}

if "a" not in d:
    d["a"] = 0

d["a"] += 1
# ~

Use collections.Counter

from collections import Counter

a = "abcabcz"
c = Counter(a)  # Counter({'a': 2, 'b': 2, 'c': 2, 'z': 1})

c["a"]  # 2
c["b"]  # 2
c["c"]  # 2
c["z"]  # 1
c["x"]  # 0

References