Python Part I · Module 7 · Sets
What a Set Is
Every regression review opens with the same two questions: what broke since yesterday, and what got fixed. Both are questions about membership — which names are in this group and not in that one — and neither has anything to do with position or with how many times a name appears. Python has a collection built for exactly that shape of question. It stores each distinct value once, it offers no positional order at all, and understanding why those two properties go together is the difference between using a set well and treating it as a list with the duplicates knocked out. This chapter introduces the collection; the two questions get their one-line answers three chapters from now.
Foundation14 min readPythonPart IFundamentalsSetsCollections
Part I · Module 7 · Chapter 7.1 · What a Set Is
1. The Question That Needs a Different Collection
Yesterday's run failed three tests. Today's run failed three tests. Some of them are the same tests.
yesterday = {"reset", "timeout", "crc"}
today = {"timeout", "crc", "interrupt"}The questions a reviewer actually asks are:
- What is failing today that was not failing yesterday? (a new problem)
- What was failing yesterday and is not failing today? (something got fixed)
- What is failing in both? (the persistent ones)
Notice what those questions do not involve. They do not ask which test failed first. They do not ask how many times a name appears — a name either is in the group or it is not. Position and repetition are simply not part of the question.
A list would carry both anyway, and you would have to work around them. The collection written above is a set, and it is built for exactly this shape of question. Chapter 7.4 answers all three in one line each. This chapter is about what the collection is, because the operations only make sense once that is clear.
2. Writing One Down
A set literal is written with curly braces and commas:
failing = {"reset", "timeout", "crc"}
print(type(failing))
print(len(failing))<class 'set'>
3Type set, three elements. The braces are new; the commas do the same separating job they did in lists and tuples.
Two properties define the collection, and both are worth stating precisely before anything else.
A set stores each distinct value once. This is not a cleanup step Python performs afterwards — it is what the collection means:
results = {"PASS", "FAIL", "PASS"}
print(len(results))
print("PASS" in results)
print("FAIL" in results)2
True
TrueThree values written, two elements stored. "PASS" is in the set; writing it twice did not make it more in the set. There is no concept of "how many times" — membership is a yes-or-no property.
A set has no positional order. There is no first element and no last one, and Section 7 shows what that costs you. It also explains why the first property and the second go together: a collection organised for answering is this present? does not need to remember where anything sat.
3. Showing What Is Inside
Here is the one habit this module needs you to adopt early, and the reason for it is a real property of Python rather than a style preference.
Because a set has no positional order, the order in which Python displays one is not something you can rely on. It may look stable within a single run and differ in the next. Writing a tutorial — or a test, or a report — that depends on that order produces something which works today and fails for no visible reason later.
So when you want to see what is in a set, ask for it in a defined order:
failing = {"reset", "timeout", "crc"}
print(sorted(failing))['crc', 'reset', 'timeout']sorted() is the built-in function from Chapter 5.5. It accepts any collection, leaves it alone, and returns a new list in order — so the result is a list, printed with square brackets, and it is the same every time.
results = {"PASS", "FAIL", "PASS"}
print(sorted(results))['FAIL', 'PASS']Two elements, alphabetically. That output is reproducible, which is what makes it worth printing.
4. Equality Is About Membership
The clearest demonstration that order is not part of a set's value:
print({"a", "b"} == {"b", "a"})TrueTwo sets are equal when they have the same members. The order you typed them in is not part of the value, so it cannot affect equality. Compare that with Chapter 5.1, where ["a", "b"] == ["b", "a"] was False — for a list, order is part of the value.
Equality is still strict about everything else:
print({"a", "b"} == {"a", "b", "c"})
print({"a", "b"} == ["a", "b"])False
FalseDifferent members, so not equal. And a set is never equal to a list, even with the same contents — the same rule Chapter 6.1 established between tuples and lists.
5. The Empty Set Trap
This one catches people and the fix is a single character of knowledge.
Empty braces do not make an empty set:
print(type({}))
print(type(set()))<class 'dict'>
<class 'set'>{} is an empty dictionary — a different collection, which Module 8 teaches. The braces were given to dictionaries first, so a set needs another way to say "empty":
empty = set()
print(empty)
print(len(empty))set()
0Note how Python displays it: set(), not {}. It is telling you which collection it is, precisely because the braces would be ambiguous.
And the two are not equal, which is worth confirming rather than assuming:
print(set() == {})FalseThat is everything Module 7 needs to say about dictionaries. Module 8 owns them.
6. Membership: The Operation Sets Exist For
You have known this operator since Chapter 3.6, and on a set it is the natural question rather than an incidental one:
failing = {"reset", "timeout", "crc"}
print("reset" in failing)
print("smoke" in failing)
print("smoke" not in failing)True
False
TrueAs with lists in Chapter 5.1, in compares whole elements — "res" in failing would be False, because no element is "res".
Chapter 7.2 is devoted to membership and is where the practical consequences live. For now, notice only that every output in this section is a plain True or False: deterministic, order-free, and exactly the kind of answer a set is organised to give.
One safe display case, since it comes up: a set with a single element has only one possible ordering, so printing it directly is unambiguous.
print({"solo"}){'solo'}That is the brace notation displayed back to you, with no ordering question to get wrong.
7. No Indexing, and Why That Follows
A set does not support positional access:
items = {"clk", "reset_n"}
print(items[0])TypeError: 'set' object is not subscriptableSlicing fails identically:
items = {"clk", "reset_n"}
print(items[0:1])TypeError: 'set' object is not subscriptableSubscriptable is the word Chapter 6.2 met: supports the square-bracket operation. Strings, lists and tuples are; a set is not.
This is worth a moment, because it is a consequence rather than a missing feature. items[0] asks for the element at position 0. A set has no positions, so the request has no meaning — there is no element that is "the first one" for Python to return. Refusing is the only correct answer; inventing one would be inventing an order the collection does not have.
8. What Is Allowed Inside
Strings, numbers and tuples all work as elements:
mixed = {"clk", 42, ("smoke", 1234)}
print(len(mixed))
print(("smoke", 1234) in mixed)3
TrueA list does not:
print({["smoke", 1234]})TypeError: unhashable type: 'list'You met unhashable in Chapter 6.1, where a list could not be a dictionary key. It is the same requirement here and for the same underlying reason: a set finds things by a value computed from the element, and that value has to stay put. If the element could change, the set would lose track of it.
The tempting rule — "sets can hold immutable values" — is good intuition and not quite the real rule. The real requirement is hashable, and the difference shows up here:
print({("smoke", [1, 2])})TypeError: unhashable type: 'list'That is a tuple, and it was still rejected. Read the message: the offending type is list — Python looked inside. Chapter 6.4 established exactly this: a tuple fixes which values its positions refer to but does not freeze those values, so a tuple holding a list can still change underneath you.
A set cannot be an element either, for the same reason — a set is itself changeable, as Chapter 7.3 will show:
print({{"a"}})TypeError: unhashable type: 'set'9. Building a Set From Something Else
set() converts, exactly as list() and tuple() did in Chapters 5.9 and 6.6:
names = ["clk", "reset_n", "clk"]
unique = set(names)
print(len(unique))
print(sorted(unique))2
['clk', 'reset_n']Three names in, two elements out — the duplicate did not become a second member, because a set has no way to hold one value twice.
A string supplies its characters, so converting one gives the distinct characters:
print(len(set("aba")))
print(sorted(set("aba")))2
['a', 'b']Two distinct characters from three. Chapter 7.6 is about when this conversion is the right move and what it costs — because something was lost here, and noticing what is the point of that chapter.
10. Predict, Run, Observe
Predict all seven before reading on. Every answer here is deterministic — a count, a boolean or a sorted list.
tags = {"WARN", "INFO", "WARN", "ERROR"}
print(len(tags))
print("WARN" in tags)
print("warn" in tags)
print(sorted(tags))
print({"a", "b"} == {"b", "a", "b"})
print(len(set("hello")))
print(type({}) == type(set()))The verified output:
3
True
False
['ERROR', 'INFO', 'WARN']
True
4
Falselen(tags) is 3, not 4 — "WARN" appears once however many times it is written.
"warn" is False on case, exactly as every membership test since Chapter 4.6.
The fifth is True: {"b", "a", "b"} has two distinct members, so it equals {"a", "b"}. Order and repetition both drop out of the comparison.
len(set("hello")) is 4 — h, e, l, o, because the second l is the same character.
And the last is False, which is Section 5: {} is a dictionary and set() is a set.
11. Exercises
Exercise 1 — write three sets. Create and print the length of: a set of four distinct signal names, a set built from a list containing a repeat, and an empty set. Use sorted() if you want to see any contents.
Exercise 2 — predict the length. Without running anything, give len() for each, then verify:
(a) {"a", "b", "c"} (b) {"a", "a", "a"} (c) set() (d) set("aabbcc") (e) set(["x", "y", "x"]) (f) {1, 2, 2, 3}
Exercise 3 — equality. Say True or False for each, then explain the two that would differ if these were lists:
(a) {"a", "b"} == {"b", "a"} (b) {"a", "b"} == {"a", "b", "a"} (c) {"a"} == ["a"] (d) set() == {}
Exercise 4 — the empty trap. A colleague writes seen = {} and then reports that seen is behaving strangely. Explain in two sentences what they actually created, how type() would reveal it, and give the corrected line.
Exercise 5 — read the error. Run {["a"]} and write down the exact message. Then predict whether {("a",)} and {("a", ["b"])} succeed, verify both, and explain the difference in two sentences.
Exercise 6 — why no indexing. Explain in three sentences why my_set[0] raises rather than returning some element, referring to what a set does and does not promise. Do not use the word "feature".
Exercise 7 — show the contents. Given tags = {"ERROR", "WARN", "INFO"}, write the line that displays its contents reproducibly, name the type of what that line prints, and say in one sentence why printing tags directly is a worse choice in a tutorial or a test.
Exercise 8 — the right collection. For each, say whether a set or a list fits the question being asked, in one sentence each: (a) the order tests must run in (b) which tests failed at least once (c) every line of a log file (d) which signal names appeared anywhere in a report
12. Summary
- A set is a collection of distinct values with no positional order. Both properties are what it means, not side effects.
- Write one with braces and commas:
{"reset", "timeout"}. - Each distinct value is stored once.
len({"PASS", "FAIL", "PASS"})is 2 — membership is yes-or-no, never a count. {}is an empty dictionary. The empty set isset(), and Python displays it asset(). Module 8 teaches dictionaries.- Unordered means no positional-order contract — not random. Never rely on the order a set displays or iterates in.
- Show contents with
sorted(), which returns a new list in a defined order and is reproducible. Uselen(),inand==for checks. ==compares members only, so{"a", "b"} == {"b", "a"}isTrue. A set is never equal to a list.- No indexing or slicing —
TypeError: 'set' object is not subscriptable— because there are no positions for[0]to refer to. - Elements must be hashable. Strings, numbers and tuples of hashable elements qualify; lists and sets do not. "All tuples are hashable" is false —
{("smoke", [1, 2])}raises. set(x)converts any collection, keeping each distinct value once.
The next chapter takes the one operation a set is best at and shows what it is worth.
Where this fits
Part of the Python curriculum.
