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.
Before you write your own
1. Predict. Six visits, but on how many different days?
visits = ["mon", "tue", "mon", "wed", "tue", "mon"]
days = set(visits)
print(len(visits), len(days))
print("thu" in days)
print(sorted(days))
print(sorted({"a", "b", "c"} - {"b"}))Check your prediction
6 3
False
['mon', 'tue', 'wed']
['a', 'c']A set can’t hold duplicates, so the three "mon"s become one. Sets have no
order of their own, which is why every line that shows a set’s contents goes
through sorted first.
2. Fix. This should collect the unique tags and print ['python', 'tips'].
tags = {}
for tag in ["python", "tips", "python"]:
tags.add(tag)
print(sorted(tags))Check your fix
tags = set()
for tag in ["python", "tips", "python"]:
tags.add(tag)
print(sorted(tags))['python', 'tips']{} is an empty dictionary, and dictionaries have no add. The error message
— 'dict' object has no attribute 'add' — names the real type, which is the
clue.
3. Choose. No code for this one. For each job, which of list, tuple, dictionary or set would you reach for, and why?
- The steps of a recipe, in order.
- Looking up a customer’s email address by their username.
- Remembering which words you’ve already seen in a long text.
- A colour, as red, green and blue values.
Check your choices
- List — order matters, and you might add a step.
- Dictionary — you’re looking something up by a name.
- Set — the only question is “have I seen this?”, and
inon a set is instant. - Tuple — a fixed number of values that together describe one thing.
If you picked differently and can say why, that’s worth more than matching. A list of words would work for (3) — it would just get slow as it grew.
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. Your output should be:
Between them: 5
Both read: ['Solaris', 'Ubik']
Ada only: ['Dune', 'Piranesi']ada = ["Dune", "Piranesi", "Solaris", "Ubik"]
alan = ["Solaris", "Ubik", "Neuromancer"]
# Your code hereHint 1: the idea
All three questions are about groups: in either list, in both, in Ada’s only. Those are exactly the set operators above — once each list is a set.
Hint 2: a first step
Convert once: ada_set = set(ada) and alan_set = set(alan). Then
len(ada_set | alan_set) answers the first question. sorted() turns a set
into an alphabetical list for printing.
Show 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.