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