Sets
Unique items, fast membership tests
In this lesson
A set is an unordered collection of unique items. Sets are brilliant for two jobs: removing duplicates, and checking very quickly whether something is in a collection.
Explain it like I’m 5
A set is a bag where every item is different. Put the same thing in twice and the bag still only has one. You can’t ask for ‘the third item’ because the bag has no order.
Uniqueness and no order
Create a set with curly braces: colors = {"red", "green", "red"} becomes just {"red", "green"} — duplicates vanish automatically. Sets have no index and no guaranteed order, so you can’t do colors[0]. What you can do is add, remove, loop, and test membership.
colors = {"red", "green", "red", "blue"}
print(len(colors))
print(sorted(colors))
3 ['blue', 'green', 'red']
Removing duplicates from a list
The quickest way to dedupe is set(my_list), which keeps one of each value. Wrap it back in list() if you need a list again: list(set(items)). (Order isn’t preserved, so only use this when order doesn’t matter.)
nums = [1, 2, 2, 3, 3, 3, 4]
unique = set(nums)
print(sorted(unique))
print("Has 3?", 3 in unique)
print("Has 9?", 9 in unique)
[1, 2, 3, 4] Has 3? True Has 9? False
Fast membership tests
Checking x in big_list scans the list item by item — slow for large data. Checking x in big_set is near-instant regardless of size, because sets are built for exactly this. If your code does lots of ‘is this present?’ checks, a set is the right tool.
Set maths: union, intersection, difference
Sets support handy operations. a | b (union) is everything in either. a & b (intersection) is what’s in both. a - b (difference) is what’s in a but not b. These answer questions like ‘which tags do these two posts share?’ in one line.
monday = {"Ada", "Bo", "Cy"}
tuesday = {"Bo", "Cy", "Di"}
print("Both days:", sorted(monday & tuesday))
print("Either day:", sorted(monday | tuesday))
print("Only Monday:", sorted(monday - tuesday))
Both days: ['Bo', 'Cy'] Either day: ['Ada', 'Bo', 'Cy', 'Di'] Only Monday: ['Ada']
Two friends list movies they've seen. Print the movies they've both seen, sorted.
alex = {"Up", "Coco", "Soul", "Brave"}
sam = {"Soul", "Brave", "Luca"}
# print movies both have seen, sorted
Intersection is alex & sam; wrap it in sorted() for a tidy printout.
alex = {"Up", "Coco", "Soul", "Brave"}
sam = {"Soul", "Brave", "Luca"}
print(sorted(alex & sam))
['Brave', 'Soul']
Changing sets and checking subsets
Sets are mutable, so you can edit them after creating: .add(x) puts one item in, .discard(x) removes one without complaining if it’s absent, and .update([...]) adds many at once.
You can also compare whole sets: a <= b asks ‘is every item of a also in b?’ (subset), and a >= b asks the reverse (superset). It’s a one-liner for questions like ‘has the user completed all the required topics?’.
seen = {"intro", "loops"}
seen.add("lists")
seen.discard("missing") # no error if absent
print(sorted(seen))
required = {"intro", "loops"}
print(required <= seen)
['intro', 'lists', 'loops'] True
Common mistake: Expecting sets to keep order or support indexing
Sets look like lists with curly braces, but they’re unordered, so s[0] raises a TypeError.
If you need order or positions, use a list. Use a set only when you care about uniqueness and membership, not order.
What happens to duplicate values in a set?
Sets store only unique values, so duplicates are automatically removed.
Which operation gives items present in BOTH sets a and b?
& is intersection — the items common to both sets.
Mini exercise (medium)
Given two lists of email addresses (signups and confirmed), print how many people signed up but have not confirmed.
Do it here. Finish the code below and press Run to try your answer right in the browser — no setup needed.
signups = ["[email protected]", "[email protected]", "[email protected]", "[email protected]"]
confirmed = ["[email protected]", "[email protected]"]
not_confirmed = 0 # TODO: convert both to set(), subtract, then take len()
print(not_confirmed)
Convert both to sets and use the difference: set(signups) - set(confirmed).
signups = ["[email protected]", "[email protected]", "[email protected]", "[email protected]"]
confirmed = ["[email protected]", "[email protected]"]
not_confirmed = len(set(signups) - set(confirmed))
print(not_confirmed)
2
__no_output_bypass__ = True
import ast as _ast
_calls = [n.func.id for n in _ast.walk(_ast.parse(__user_code__)) if isinstance(n, _ast.Call) and isinstance(n.func, _ast.Name)]
assert _calls.count("set") >= 2, "use set() on both lists and subtract to find who didn't confirm"
__no_output_bypass__ = False
assert not_confirmed == 2, "2 people signed up but did not confirm"
print("✓ Correct!")