A set is an unordered collection with no duplicates. Adding something twice changes nothing.
tags = {"python", "beginner", "python", "tutorial"}
print(tags)
print(len(tags))
# The usual way to make one: from another collection.
visitors = ["ada", "alan", "ada", "grace", "alan"]
unique = set(visitors)
print(unique)
print(len(unique), "unique visitors out of", len(visitors))Empty braces make a dictionary
{} is an empty dictionary, not an empty set — dictionaries got the syntax
first. For an empty set, write set().
Membership is the superpower
Checking x in collection on a list means looking at every item. On a set it is
effectively instant, however large the set grows — the same mechanism that makes
dictionary lookups fast.
import time
big_list = list(range(200_000))
big_set = set(big_list)
target = 199_999
start = time.perf_counter()
target in big_list
list_time = time.perf_counter() - start
start = time.perf_counter()
target in big_set
set_time = time.perf_counter() - start
print(f"list: {list_time * 1000:.3f} ms")
print(f"set: {set_time * 1000:.3f} ms")If your code says if item in some_list: inside a loop, converting that list to
a set once beforehand is often the single biggest speed-up available to you.
Set arithmetic
Sets answer questions about groups: what is in both, what is in only one, what is in either.
morning = {"ada", "alan", "grace"}
evening = {"grace", "katherine", "alan"}
print(morning & evening) # intersection - in both
print(morning | evening) # union - in either
print(morning - evening) # difference - morning only
print(morning ^ evening) # symmetric difference - in exactly one
print(morning.isdisjoint({"linus"}))
print({"ada"} <= morning) # is it a subset?Each operator has a spelled-out method too — .intersection(), .union(),
.difference() — which reads better in code that others will maintain.
Changing a set
langs = {"python"}
langs.add("rust")
langs.add("python") # already there, no effect
print(langs)
langs.discard("go") # missing is fine
langs.remove("rust") # missing raises KeyError
print(langs)Sets are unordered, so there is no indexing: langs[0] is an error. If you need
order, sort into a list when you need it: sorted(langs).
Choosing a collection
| You need | Use | Why |
|---|---|---|
| An ordered, changing sequence | list | Indexing, slicing, appending |
| A fixed record of related values | tuple | Immutable, unpackable, safe as a dict key |
| Lookup by name or id | dict | Instant access by key |
| Uniqueness or fast membership | set | No duplicates, instant in |
Most bugs of the “this is slow” or “why is it duplicated” kind trace back to this table.
Compare two reading lists
Two people list the books they have read. Print how many books they have read between them, which they have both read, and which only Ada has read — each alphabetically.
ada = ["Dune", "Piranesi", "Solaris", "Ubik"]
alan = ["Solaris", "Ubik", "Neuromancer"]
# Your code hereShow one solution
ada = ["Dune", "Piranesi", "Solaris", "Ubik"]
alan = ["Solaris", "Ubik", "Neuromancer"]
ada_set = set(ada)
alan_set = set(alan)
print("Between them:", len(ada_set | alan_set))
print("Both read: ", sorted(ada_set & alan_set))
print("Ada only: ", sorted(ada_set - alan_set))Convert to sets once, then let the operators do the work. The same three
answers written with loops and if x in y would run to fifteen lines and be
slower.
Day 3 is done
You now have all four core containers. Almost every Python program is some arrangement of lists of dictionaries, dictionaries of lists, and sets used to answer “have I seen this before?”.
Tomorrow you learn to name behaviour, not just data.
What you learned
- Sets hold unique, unordered items;
set()makes an empty one. inis fast on sets and dictionaries, slow on lists.&,|,-,^compare groups.add,discard(safe) andremove(strict) change a set.- Pick the container by the question you need to ask of it.