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]:
| Operation | Time Complexity | Space Complexity | Syntax |
|---|---|---|---|
| Size | len(d) | ||
| Get item | d[k] or d.get(k) | ||
| Set item | d[k] = v | ||
| Delete item | del d[k] | ||
| Key in dict | k in d | ||
| Iterate | for 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