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']
True
False

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.

python — playgroundlive
⌘/Ctrl + Enter to run

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: 6
unique 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 lunch
Noor: already served
Mina: already served
Ira: 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.

+50 XP on completion