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

PY create.py
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({}))
Output
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

PY dedupe.py
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)
Output
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

PY modify.py
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))
Output
['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:

PY membership.py
banned = set(range(0, 3_000_000, 3))

print(2_999_997 in banned)
print(2_999_998 in banned)
Output
True
False

If you repeatedly check whether things are "in" a large collection, convert it to a set first.

Set operations

PY operations.py
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))
Output
['css', 'docker', 'html', 'javascript', 'python', 'react', 'sql']
['javascript']
['css', 'html', 'react']
['css', 'docker', 'html', 'python', 'react', 'sql']
OperatorMethodResult
a | ba.union(b)Everything in either
a & ba.intersection(b)Only what's in both
a - ba.difference(b)In a but not b
a ^ ba.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

PY compare.py
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")
Output
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:

PY hashable.py
points = {(0, 0), (1, 2)}
print(len(points))
bad = {[1, 2]}
Output
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

PY comprehension.py
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))
Output
['x.com', 'y.org']

Try it yourself

Try it yourself
Console output