Python Dicts and Sets


  • Description: dict (insertion-ordered hash map since 3.7), set/frozenset, set algebra, and the collections module, defaultdict, Counter, OrderedDict, ChainMap
  • My Notion Note ID: K2A-D1-6
  • Created: 2022-06-08
  • Updated: 2026-05-11
  • License: Reuse is very welcome. Please credit Yu Zhang and link back to the original on yuzhang.io

Table of Contents


1. dict

  • Hash map, equivalent of C++ std::unordered_map

1.1 Insertion Order Is Guaranteed (3.7+)

  • 3.7+: iteration order matches insertion order (language guarantee)
  • 3.6: same behavior as CPython implementation detail
  • Lookup remains amortized O(1)
d = {"a": 1, "b": 2, "c": 3}
list(d)                  # ['a', 'b', 'c'] , always

1.2 Construction and Lookup

d = {"a": 1, "b": 2}
d = dict(a=1, b=2)
d = dict([("a", 1), ("b", 2)])
d = {k: 0 for k in "abc"}     # dict comprehension

d["a"]                  # 1
d["z"]                  # KeyError
d.get("z")              # None , no exception
d.get("z", 0)           # 0  , default
  • Use d.get() when missing-is-normal
  • Use d[k] when missing-is-a-bug

1.3 Useful Methods

d["new"] = 5            # insert or update
del d["a"]              # KeyError if missing
d.pop("a", None)        # remove with default, no exception
d.popitem()             # remove last (k, v), LIFO
d.setdefault("k", [])   # get d["k"] or insert []
d.update(other_dict)
d.clear()
"k" in d                # membership

1.4 Views: keys, values, items

for k in d: ...                # iterates keys by default
for k in d.keys(): ...
for v in d.values(): ...
for k, v in d.items(): ...
  • Views are live, reflect later mutations of d
  • Set-like: d.keys() & other.keys() works

1.5 Merging Dicts

merged = a | b           # 3.9+: new dict; b's keys win on conflict
a |= b                   # 3.9+: update in place
merged = {**a, **b}      # legacy; same semantics

2. set and frozenset

s = {1, 2, 3}            # set literal (note: {} is an empty DICT!)
s = set()                # empty set
s = set([1, 1, 2])       # {1, 2}
fs = frozenset([1, 2])   # immutable, hashable
  • Set elements must be hashable
  • frozenset itself is hashable → can be a dict key

2.1 Set Algebra

Op Method Meaning
| union elements in either
& intersection elements in both
- difference in left, not in right
^ symmetric_difference in exactly one
<= issubset every left elt is in right
>= issuperset every right elt is in left
< proper subset subset and not equal
a = {1, 2, 3}
b = {2, 3, 4}
a | b                    # {1, 2, 3, 4}
a & b                    # {2, 3}
a - b                    # {1}
a ^ b                    # {1, 4}

a.add(4)
a.discard(99)            # no error if absent
a.remove(2)              # KeyError if absent
a.update([5, 6])

3. collections

3.1 defaultdict

  • Auto-creates missing values with a zero-arg factory, avoids setdefault boilerplate
from collections import defaultdict
groups = defaultdict(list)
for name, team in roster:
    groups[team].append(name)       # no KeyError on first append

freq = defaultdict(int)
for w in words:
    freq[w] += 1

3.2 Counter

  • dict subclass for counting
from collections import Counter
c = Counter("mississippi")
c                          # Counter({'i': 4, 's': 4, 'p': 2, 'm': 1})
c.most_common(2)           # [('i', 4), ('s', 4)]
c["x"]                     # 0, missing keys give 0, not KeyError
c1 + c2                    # add counts
c1 - c2                    # subtract (clamped at zero)
c1 & c2                    # min of counts
c1 | c2                    # max of counts

3.3 OrderedDict

  • Largely legacy since 3.7
  • Still useful for move_to_end or order-sensitive equality (regular dict compares unordered)
from collections import OrderedDict
od = OrderedDict([("a", 1), ("b", 2)])
od.move_to_end("a")               # 'a' becomes the last key
od.move_to_end("a", last=False)   # ... or the first

3.4 ChainMap

  • View over multiple mappings, searched in order
  • Useful for layered config (CLI > env > defaults)
from collections import ChainMap
config = ChainMap(cli_args, env_vars, defaults)
config["timeout"]          # first hit wins
  • Writes go to the first map; the underlying maps are not modified