Sets
Intermediate · 18 min read · ▶ live playground · ✦ checkpoint
Sometimes the question is not "where is this value?" or "what label stores it?" Sometimes the whole question is: "Have I seen this before?" A set is an unordered, mutable collection of unique values, where unordered means there is no first item and unique means the same value can appear only once.
A set keeps one of each value
Think of a set as a guest list at the door. A name is either on the list or it is not. Writing "Mina" twice does not make two Minas, and nobody gets seat number 0.
A set literal is a set written directly in code with curly braces. It looks a little like a dictionary from lesson 6.1, but there are no key: value pairs.
favorite_snacks = {"apples", "tea", "apples", "mangoes"}
empty_set = set()
empty_braces = {}
print(sorted(favorite_snacks))
print(type(empty_set))
print(type(empty_braces))
print("apples" in favorite_snacks)['apples', 'mangoes', 'tea']<class 'set'><class 'dict'>True
The duplicate "apples" disappears because sets keep one copy of each value. sorted(favorite_snacks) returns a list only so the display is stable; the set itself does not promise an order.
The empty set is set(). Empty braces {} create an empty dictionary, not an empty set, because dictionaries got that syntax first.
Add and remove values
Sets are mutable, so the same set object can grow and shrink. add(value) puts one value into a set. update(collection) adds each item from another collection.
team = {"Mina", "Ravi"}
team.add("Noor")
team.add("Mina")
team.update(["Ira", "Tao"])
team.discard("Ravi")
team.discard("Sam")
print(sorted(team))
print("Mina" in team)
print("Ravi" in team)['Ira', 'Mina', 'Noor', 'Tao']TrueFalse
Adding "Mina" again changes nothing. A set is still a one-copy collection.
discard(value) removes a value if it is present and does nothing if it is missing. remove(value) also removes a value, but it raises KeyError if the value is missing; full exception handling comes in lesson 9.3. Use discard() when "already gone" is fine.
Compare groups with set math
Set math is where sets start to feel powerful. A union means values in either set, an intersection means values in both sets, a difference means values in the left set but not the right set, and a symmetric difference means values in exactly one set but not both.
signed_up = {"Mina", "Ravi", "Noor", "Ira"}
checked_in = {"Ravi", "Ira", "Tao"}
print("union:", sorted(signed_up.union(checked_in)))
print("intersection:", sorted(signed_up.intersection(checked_in)))
print("difference:", sorted(signed_up.difference(checked_in)))
print("symmetric:", sorted(signed_up.symmetric_difference(checked_in)))union: ['Ira', 'Mina', 'Noor', 'Ravi', 'Tao']intersection: ['Ira', 'Ravi']difference: ['Mina', 'Noor']symmetric: ['Mina', 'Noor', 'Tao']
Read the story, not just the method names:
union()asks, "Who is mentioned anywhere?"intersection()asks, "Who is in both groups?"difference()asks, "Who signed up but has not checked in?"symmetric_difference()asks, "Who is on exactly one list?"
Python also has short operators for the same ideas: | for union, & for intersection, - for difference, and ^ for symmetric difference. The method names are easier to read while the idea is new.
Try it - compare two practice groups
Change the names in each group. Then add the same name to both sets and watch how that changes the intersection and symmetric difference.
Deduplicate lists
Deduplication means removing repeated values so each value appears once. If order does not matter, set() is the cleanest deduplication tool you have.
raw_names = ["Mina", "Ravi", "Mina", "Noor", "Ravi", "Ira"]
unique_names = set(raw_names)
print(sorted(unique_names))
print("raw count:", len(raw_names))
print("unique count:", len(unique_names))['Ira', 'Mina', 'Noor', 'Ravi']raw count: 6unique count: 4
That one conversion gives you the unique names. The trade-off is order: a set cares about membership, not position. If your output needs the original order, use a loop and an accumulator list instead. The next lesson gives you a practical guide for choosing between lists, tuples, sets, and dictionaries.
Use sets for membership tests
A membership test is an in check that asks whether a value is inside a collection. Sets are built for this question: "Is this value present?"
served_lunch = {"Mina", "Ravi", "Noor"}
visitors = ["Tao", "Noor", "Mina", "Ira"]
for visitor in visitors:
if visitor in served_lunch:
print(f"{visitor}: already served")
else:
print(f"{visitor}: needs lunch")Tao: needs lunchNoor: already servedMina: already servedIra: needs lunch
You are not asking where "Noor" sits. You only need a yes-or-no answer. For repeated checks, a set is usually faster than walking a list one item at a time, and the performance intuition behind that choice comes in lesson 6.3.
Freeze a set with frozenset
A frozenset is an immutable set, meaning it keeps the set rules but cannot be changed in place after creation. Use it when the group of unique values should stay fixed.
default_toppings = frozenset(["basil", "tomato", "cheese", "basil"])
print(sorted(default_toppings))
print("basil" in default_toppings)
print(type(default_toppings))['basil', 'cheese', 'tomato']True<class 'frozenset'>
frozenset still removes duplicates and supports membership tests. What it does not support is mutation: no add(), no discard(), no in-place editing. If you need a set that changes over time, use set. If you need a set-shaped value that should stay fixed, use frozenset.
Checkpoint
Answer all three to mark this lesson complete
Sets give you a clean way to handle uniqueness and compare groups. Next, you put lists, tuples, dictionaries, and sets side by side so you can choose the right collection on purpose.