Skip to content
VLSI Mentor

Python Part I · Module 7 · Sets

Checking Membership and Looping Over a Set

A set exists to answer one question well: is this value present? That question is asked with an operator you have known since Module 3, so the syntax costs you nothing — what is new is understanding why this collection is the right place to ask it, and that answer is about how a set finds things rather than about any feature it has. The second half of the chapter walks through a set one element at a time, which is the only way in, since there are no positions to index. That walk comes with a condition attached, and rather than asking you to take it on trust this chapter measures it.

Foundation13 min readPythonPart IFundamentalsSetsMembership

Part I · Module 7 · Chapter 7.2 · Checking Membership and Looping Over a Set

1. The Question, and the Operator You Already Have

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
failed = {"burst", "reset"}
print("burst" in failed)
print("smoke" in failed)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
True
False

That is in from Chapter 3.6, unchanged. It produces a bool, so everything Module 3 said about combining booleans applies:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
failed = {"burst", "reset"}
found = "reset" in failed
print(found, type(found))
print(found and (len(failed) == 2))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
True <class 'bool'>
True

not in asks the opposite and reads better than negating:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
failed = {"burst", "reset"}
print("smoke" not in failed)
print("burst" not in failed)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
True
False

And the matching rules are the ones you already know — whole elements, case-sensitive:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
failed = {"burst", "reset"}
print("bur" in failed)
print("BURST" in failed)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
False
False

"bur" is not an element, and "BURST" is a different string from "burst". Neither of those is new; both have been true since Chapter 4.6.

2. Same Answer, Different Work

Put the two collections side by side. They agree, always:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
failed_list = ["burst", "reset"]
failed_set = {"burst", "reset"}
print("burst" in failed_list)
print("burst" in failed_set)
print(("burst" in failed_list) == ("burst" in failed_set))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
True
True
True

The answers match. What differs is how each one arrives at the answer.

A list is a sequence, so in checks its elements one after another until it finds a match or runs out. To decide that something is absent, it has to look at every element — there is nowhere else the value could be hiding.

A set is organised around the values themselves. Rather than walking the collection, it computes where the value would have to be and looks there. That is what the hashability requirement from Chapter 7.1 was for: the element's value determines where it lives, so finding it does not require a search.

The better reason to reach for a set is usually not speed at all. It is that a set says what the data is: a group of distinct names where membership is the question. Chapter 7.6 turns that into a decision.

3. A Membership Table

The shape this is most used for — a fixed group of accepted values, checked against as things arrive:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
supported = {".sv", ".v", ".vhd"}
print(".sv" in supported)
print(".py" in supported)
print(len(supported))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
True
False
3

Three accepted extensions, and the check is one readable line per question. Compare the alternative from Chapter 4.10 — name.endswith((".sv", ".v", ".vhd")) — which answers a different question about how a string ends. This one asks whether an exact value is in a known group.

Every output in this section is a boolean or a count: deterministic, reproducible, and completely independent of any ordering.

4. Getting At the Elements

Chapter 7.1 showed that a set has no positions:

Azvya Education Pvt. Ltd.VLSI Mentor
BROKEN ON PURPOSE
items = {"clk", "reset_n"}
print(items[0])
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
TypeError: 'set' object is not subscriptable

So if you want to do something with each element, indexing is not available. Walking the collection is — and the syntax is the one Chapter 5.4 established, unchanged:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
sigs = {"valid", "clk", "reset_n"}
for s in sorted(sigs):
    print(s)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
clk
reset_n
valid

One pass per element, the loop variable referring to one element at a time. Everything Chapter 5.4 said about the loop variable being a name, about the indented body, and about zero elements meaning zero passes is true here without modification.

But look carefully at what that loop was actually given. It is not looping over sigs — it is looping over sorted(sigs), and the next section is why.

5. The Order Is Not Yours

A set can be looped over directly:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
failed = {"burst", "reset", "timeout"}
for name in failed:
    print(name)

This runs, and prints each of the three names on its own line. This chapter will not tell you what order they come out in, and not out of caution — because there is no answer to give.

Here is the measurement. That exact three-line program was run in twelve separate Python processes, and the output was compared:

RunsDistinct orders observed
125

Five different orders, from identical code, on the same machine, with the same Python. Among them were burst, reset, timeout, reset, timeout, burst and timeout, reset, burst.

Nothing was random and nothing was broken. Python does not promise an order for a set, and in the absence of a promise the order falls out of internal details that are free to differ between runs.

The fix is the one Chapter 7.1 introduced, and it is one word:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
failed = {"burst", "reset", "timeout"}
for name in sorted(failed):
    print(name)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
burst
reset
timeout

That output is reproducible. sorted() returns a new list in a defined order, the loop walks that list, and the set is untouched.

Here is that second case, where the result is deliberately independent of order:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
sigs = {"valid", "clk", "reset_n"}
total = 0
for s in sigs:
    total += len(s)
print(total)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
15

Five plus three plus seven, in whatever order those additions happened. Addition does not care, so neither does the answer — 15 every time.

And when the question is just how many, no loop is needed at all:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
sigs = {"valid", "clk", "reset_n"}
print(len(sigs))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
3

6. Membership Inside a Loop

Combining the two halves of the chapter gives a genuinely useful shape — checking a group of required things against what was actually seen:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
required = {"clk", "reset_n"}
observed = {"clk", "reset_n", "valid"}
for r in sorted(required):
    print(r, r in observed)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
clk True
reset_n True

Each required signal, and whether it turned up. sorted(required) keeps the report in a stable order; r in observed is the per-item check.

Chapter 7.5 answers the whole question in a single line without a loop at all, which is usually what you want. Seeing the long version first is worth it, because it makes clear what that one line is actually doing.

An empty set simply runs the body zero times, exactly as an empty list did:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
none_failed = set()
for x in none_failed:
    print(x)
print("done")
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
done

Nothing failed, so nothing printed, and the script carried on. No special handling was needed — the same property Chapter 5.4 pointed out for lists.

7. Do Not Change a Set While Looping Over It

Chapter 5.4 gave this rule for lists, where breaking it silently skipped elements. On a set, Python catches you:

Azvya Education Pvt. Ltd.VLSI Mentor
BROKEN ON PURPOSE
s = {"a", "b", "c"}
for x in s:
    s.add(x + "!")
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
RuntimeError: Set changed size during iteration

A clear RuntimeError naming exactly what happened. That is a better outcome than the list version, which produced wrong results quietly — but the rule is the same and it is worth carrying as one habit rather than two:

Chapter 7.3 is about changing a set deliberately, outside any loop.

8. Predict, Run, Observe

Predict all seven before reading on. Every answer is a boolean, a count, or routed through sorted().

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
tags = {"ERROR", "WARN", "INFO"}
print("ERROR" in tags)
print("error" in tags)
print("ERR" in tags)
print("DEBUG" not in tags)
print(len(tags))
print(sorted(tags))
print(("ERROR" in tags) and ("DEBUG" in tags))

The verified output:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
True
False
False
True
3
['ERROR', 'INFO', 'WARN']
False

"error" fails on case and "ERR" fails because membership compares whole elements — neither is new, both are worth re-confirming on a set.

sorted(tags) is alphabetical and reproducible, and it prints with square brackets because it is a list, not a set.

The last line is False: the first half is True, the second is False, and and needs both. Chapter 3.5's short-circuit rule applies unchanged.

Now the question this chapter is really testing. Given:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
for t in tags:
    print(t)

What order will the three lines appear in? The correct answer is that the question has no answer. If you predicted alphabetical, you predicted sorted(). If you predicted insertion order, you predicted a list. A set promises neither.

9. Exercises

Exercise 1 — ask the membership questions. For supported = {".sv", ".v", ".vhd"}, write expressions answering: is .vhd supported, is .py not supported, how many extensions are supported, and what the supported list looks like in a stable order.

Exercise 2 — predict. Given names = {"clk", "reset_n", "valid"}, say True, False or the exact value for each, then verify: (a) "clk" in names (b) "CLK" in names (c) "cl" in names (d) "ready" not in names (e) len(names) (f) sorted(names)

Exercise 3 — the order question. A colleague's script prints a set of failing tests and they report that the report "comes out in a different order sometimes". Explain in three sentences why, say whether it is a bug in their code, and give the one-word change that fixes it.

Exercise 4 — which loop. For each task, say whether you would loop over the set directly or over sorted(set), and why: (a) print a list of failures for a human to read (b) add up the lengths of every name (c) write the names into a file another script will compare against a stored copy (d) check each name against a second collection and count how many matched

Exercise 5 — list or set for this check. You must test ten thousand incoming names against a group of five thousand known-bad names, once per incoming name. Say which collection you would hold the known-bad names in and why, in two sentences, without quoting any timings.

Exercise 6 — the empty case. Explain in two sentences why a loop over an empty set needs no special handling, and what that is worth in a script that reports failures.

Exercise 7 — mutation during iteration. Predict what happens, run it, write down the exact error, and give a one-sentence rule you would tell a colleague:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
s = {"a", "b"}
for x in s:
    s.remove(x)

Exercise 8 — reconstruct the long form. Chapter 7.5 will answer "are all required signals present?" in one line. Using only this chapter, write the loop that answers it for required = {"clk", "reset_n"} and observed = {"clk", "valid"}, printing each required name and whether it was observed.

10. Summary

  • in and not in work on a set exactly as on a list, produce a bool, compare whole elements and are case-sensitive.
  • The answers are identical; the work is not. A list checks its elements one after another; a set computes where the value would be and looks there — which is what hashability is for.
  • For a handful of elements the difference does not matter. It matters for large collections checked repeatedly. No timings, no notation: measure the real program if it ever decides anything.
  • The better reason to use a set is meaning — a group of distinct values where membership is the question.
  • A set has no positions, so a loop is the only way to reach every element. The syntax is Chapter 5.4's, unchanged.
  • The order of that loop is not promised. The same three-element set iterated in twelve processes produced five different orders.
  • Loop over sorted(my_set) whenever the order of your output matters; loop over the set directly only when it genuinely does not.
  • sorted() returns a list, which is why its output prints with square brackets.
  • An empty set runs the body zero times and needs no special handling.
  • Do not add or remove elements while looping — on a set this raises RuntimeError: Set changed size during iteration.

The next chapter changes a set on purpose.

Where this fits

Part of the Python curriculum.