Sets & Frozensets
Sets store unique hashable elements with fast membership tests. Frozensets are immutable sets you can use as dict keys or elements of other sets.
Recipe
seen: set[int] = set()
for value in [1, 2, 2, 3]:
if value not in seen:
seen.add(value)
a, b = {1, 2, 3}, {3, 4}
union = a | b
only_a = a - bWhen to reach for this:
- Deduplicating sequences before processing
- Permission/tag intersections
- Graph neighbor collections
- Finding symmetric differences between snapshots
Working Example
def unique_preserve_order(items: list[str]) -> list[str]:
seen: set[str] = set()
result: list[str] = []
for item in items:
if item not in seen:
seen.add(item)
result.append(item)
return result
def jaccard(tags_a: set[str], tags_b: set[str]) -> float:
if not tags_a and not tags_b:
return 1.0
return len(tags_a & tags_b) / len(tags_a | tags_b)
def frozen_key(groups: list[frozenset[str]]) -> dict[frozenset[str], int]:
return {group: len(group) for group in groups}
if __name__ == "__main__":
print(unique_preserve_order(["a", "b", "a", "c"]))
print(jaccard({"py", "ml"}, {"py", "web"}))
print(frozen_key([frozenset({"a", "b"}), frozenset({"c"})]))What this demonstrates:
- Manual order-preserving dedupe when
dict.fromkeysis not enough ordering control &and|for intersection and unionfrozensetas dict key for groupings
Deep Dive
How It Works
- Hash set - Same average O(1) as dict keys without values.
- Unordered - Iteration order is implementation-dependent - do not rely for semantics.
- frozenset - Immutable; hashable if elements hashable.
- Subset tests -
a <= b,a < b,a >= bfor containment. - Comprehensions -
{x for x in items if cond}builds sets directly.
Operations
| Operator | Meaning |
|---|---|
| | union |
& | intersection |
- | difference |
^ | symmetric difference |
Python Notes
# empty set - not {}
empty = set()
# bulk add
tags.update(["a", "b", "c"])
# discard vs remove - discard ignores missing
tags.discard("missing")Gotchas
{}is empty dict - Useset()for empty set. Fix:set()or{0}placeholder only if intentional.- Unhashable elements - Lists in sets fail. Fix: Convert to tuple or frozenset.
- Order assumptions - Sets do not preserve insert order for logic. Fix: dict.fromkeys trick or ordered dedupe loop.
- Tiny sets slower than list scan - Constant overhead matters for n < ~10. Fix: Profile before optimizing.
- Mutating set during iteration - Size change error. Fix: Copy
set(s)before removing in loop.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
dict.fromkeys | Order-preserving unique keys | Need set algebra |
list + in | Very small n | Large membership checks |
bloom filter | Probabilistic huge scale | Need exact membership |
pandas.unique | Column dedupe in dataframes | Plain Python script |
FAQs
set or list for uniqueness?
set when order does not matter and membership is frequent. Order-preserving dedupe uses dict or manual loop.
What is frozenset for?
Hashable set - dict keys, set elements, cache keys from frozenset of tags.
How do I intersect many sets?
from functools import reduce; reduce(set.__and__, sets) or loop with acc &= s.
issubset vs <=?
Same for sets - pick readable operator or method consistently in codebase.
Can sets hold mixed types?
Yes if all elements hashable and comparable for equality - but harms clarity; prefer homogeneous sets.
How dedupe preserve order Python 3.7+?
list(dict.fromkeys(items)) - fast idiomatic pattern.
symmetric_difference use case?
Elements in either set but not both - diffing permission grants between environments.
set comprehension vs filter?
{x for x in items if pred(x)} builds set directly without intermediate list.
Are sets thread-safe?
Individual operations atomic in CPython but compound read-modify-write needs locks.
pop from set?
set.pop() removes arbitrary element - useful only when any element acceptable.
Related
- Dictionaries - maps vs sets
- Immutability & Hashability - hash rules
- collections Module - Counter uses dict not set
- Choosing the Right Data Structure - membership patterns
Stack versions: This page was written for Python 3.14.0 (stable 3.14, maintenance 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, and uv 0.6+.