Sets
A set is an unordered collection of unique items. Sets remove duplicates automatically, check membership very quickly, and support the operations from maths — union, intersection and difference — which makes comparing groups of things easy.
Creating sets
tags = {"python", "web", "python", "data"}
print(len(tags))
print(sorted(tags))
from_list = set([3, 1, 3, 2, 1])
print(sorted(from_list))
empty = set()
print(type(empty), type({}))
3 ['data', 'python', 'web'] [1, 2, 3] <class 'set'> <class 'dict'>
Duplicates are dropped. Sets have no order, so the examples sort them before printing — the order you'd see from print(tags) isn't guaranteed. Note that {} creates an empty dictionary; use set() for an empty set.
Removing duplicates
visits = ["home", "about", "home", "blog", "about", "home"] unique = set(visits) print(len(visits), "visits,", len(unique), "unique pages") ordered_unique = list(dict.fromkeys(visits)) print(ordered_unique)
6 visits, 3 unique pages ['home', 'about', 'blog']
If you need to keep the original order while removing duplicates, dict.fromkeys does it, because dictionaries preserve insertion order.
Adding and removing
colors = {"red", "green"}
colors.add("blue")
colors.add("red")
colors.update(["yellow", "purple"])
print(sorted(colors))
colors.remove("purple")
colors.discard("orange")
print(sorted(colors))
item = colors.pop()
print(len(colors))
['blue', 'green', 'purple', 'red', 'yellow'] ['blue', 'green', 'red', 'yellow'] 3
remove raises a KeyError if the item is missing; discard silently does nothing. pop removes and returns an arbitrary item.
Fast membership tests
Checking x in some_set takes the same tiny amount of time whether the set has ten items or ten million, because sets are built on hash tables. Lists have to check items one by one:
banned = set(range(0, 3_000_000, 3)) print(2_999_997 in banned) print(2_999_998 in banned)
True False
If you repeatedly check whether things are "in" a large collection, convert it to a set first.
Set operations
frontend = {"html", "css", "javascript", "react"}
backend = {"python", "sql", "javascript", "docker"}
print(sorted(frontend | backend))
print(sorted(frontend & backend))
print(sorted(frontend - backend))
print(sorted(frontend ^ backend))
['css', 'docker', 'html', 'javascript', 'python', 'react', 'sql'] ['javascript'] ['css', 'html', 'react'] ['css', 'docker', 'html', 'python', 'react', 'sql']
| Operator | Method | Result |
|---|---|---|
a | b | a.union(b) | Everything in either |
a & b | a.intersection(b) | Only what's in both |
a - b | a.difference(b) | In a but not b |
a ^ b | a.symmetric_difference(b) | In one but not both |
The method versions accept any iterable (like a list), while the operators require both sides to be sets.
Comparing sets
required = {"name", "email"}
submitted = {"name", "email", "phone"}
print(required <= submitted)
print(required.issubset(submitted))
print(submitted >= required)
print(required.isdisjoint({"age"}))
print(sorted(required - submitted) or "nothing missing")
True True True True nothing missing
"Are all the required fields present?" becomes a single subset check.
What can go in a set
Set items must be hashable — in practice, immutable: numbers, strings, tuples, frozensets. Lists, dictionaries and other sets can't be members:
points = {(0, 0), (1, 2)}
print(len(points))
bad = {[1, 2]}
2
Traceback (most recent call last):
File "main.py", line 3, in <module>
bad = {[1, 2]}
^^^^^^^^
TypeError: cannot use 'list' as a set element (unhashable type: 'list')frozenset is an immutable set, which can itself go inside a set or be a dictionary key.
Set comprehensions
emails = ["Ana@x.com", "ben@y.org", "ana@x.com", "cleo@y.org"] domains = {e.split("@")[1].lower() for e in emails} print(sorted(domains))
['x.com', 'y.org']