Skip to content
VLSI Mentor

Python Part I · Module 7 · Sets

Subsets, Supersets and Disjoint Sets

The previous chapter took two sets and built a third from them. This one asks a different kind of question entirely: not what the overlap contains, but what the relationship between the two sets is. Did everything that was supposed to run actually run? Is this group of severities completely absent from what we saw? Both are yes-or-no questions about a whole set at once, and Python has direct ways to ask them — which matters, because the obvious way to work them out by hand is more code, easier to get wrong, and harder to read. The chapter's main hazard is the same one the difference operator had: a relationship has a direction, and reversing it gives you a plausible answer to a question you did not ask.

Foundation13 min readPythonPart IFundamentalsSetsRelationships

Part I · Module 7 · Chapter 7.5 · Subsets, Supersets and Disjoint Sets

1. A Different Kind of Question

Chapter 7.4's operations all produced a set: what broke, what was fixed, what overlapped. This chapter's questions produce a boolean:

  • Did every test that was required actually run?
  • Does this run cover everything the previous one did?
  • Is there any severity in common between these two groups?

Each is about the two sets as wholes, and the answer is yes or no. That distinction is worth holding on to — if you catch yourself computing a set and then checking whether it is empty, there is probably a direct question that says what you meant.

2. Is Everything Required Covered?

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
required = {"smoke", "reset"}
ran = {"smoke", "reset", "burst"}
print(required <= ran)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
True

required <= ran asks: is every element of required also in ran? It is, so True. Everything that had to run did, and burst ran as well, which this question does not care about.

The relationship has names. required is a subset of ran; ran is a superset of required. Both describe the same situation from opposite ends:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
required = {"smoke", "reset"}
ran = {"smoke", "reset", "burst"}
print(ran >= required)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
True

>= is the mirror: does ran contain everything in required? Same relationship, same answer, read from the other side.

Methods say it in words:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
required = {"smoke", "reset"}
ran = {"smoke", "reset", "burst"}
print(required.issubset(ran))
print(ran.issuperset(required))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
True
True

3. The Reversed Question

Write it the other way round and you get a perfectly reasonable-looking answer to a different question:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
required = {"smoke", "reset"}
ran = {"smoke", "reset", "burst"}
print(ran <= required)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
False

False, and nothing is wrong. ran <= required asks whether everything that ran was required — and burst ran without being required, so no.

That is a legitimate question (it would catch tests running that should not be), but it is not "was everything covered". Both forms return a boolean, both are valid Python, and the output gives you no hint which one you asked.

4. When Coverage Is Incomplete

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
required = {"smoke", "reset", "crc"}
ran = {"smoke", "reset"}
print(required <= ran)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
False

Something required did not run. The boolean tells you that; to find out what, use the difference from Chapter 7.4:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
required = {"smoke", "reset", "crc"}
ran = {"smoke", "reset"}
print(sorted(required - ran))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
['crc']

required - ran is exactly "required things that did not run". The two together are the whole coverage report: a yes-or-no gate, and the list of what is missing when the answer is no.

5. Subset Is About Membership, Not Size

This is the misconception worth destroying, and one verified pair does it:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
print({"z"} <= {"a", "b", "c"})
print(len({"z"}) < len({"a", "b", "c"}))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
False
True

The left set is smaller — one element against three — and it is not a subset. Its single element is not in the other set, and that is the only thing subset asks about.

Being smaller is a consequence of being a subset, never a cause. A subset can be smaller or the same size; it cannot be larger. But smallness on its own tells you nothing.

6. A Set Contains Itself, and the Empty Set Is in Everything

Two edge cases that follow from the definition rather than being special rules.

Every set is a subset of itself, because every element of it is in it:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
s = {"a", "b"}
print(s <= s)
print(s >= s)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
True
True

So <= is not a strict comparison — it allows equality, exactly as <= does on numbers. If you need to exclude the equal case, < asks for a proper subset:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
s = {"a", "b"}
print(s < s)
print({"a"} < {"a", "b"})
print({"a", "b"} < {"a", "b"})
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
False
True
False

{"a"} is a proper subset of {"a", "b"} — contained, and not equal. A set is never a proper subset of itself. In practice <= is what coverage questions want; < matters only when "and strictly smaller" is genuinely part of the question.

The empty set is a subset of every set, including itself:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
print(set() <= {"a"})
print(set() <= set())
print(set() < {"a"})
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
True
True
True

That looks like a technicality and is useful: if nothing is required, the coverage check passes without any special handling. The question "is every required test in the set that ran?" has the answer "yes, vacuously" when there are no required tests — and required <= ran gives exactly that, with no extra code.

7. Nothing in Common: isdisjoint()

A different relationship question — do these two sets share anything?

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
fatal = {"FATAL", "PANIC"}
seen = {"INFO", "WARNING"}
print(fatal.isdisjoint(seen))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
True

True means no element appears in both. Nothing fatal was seen, which is the answer a log check wants.

When they do overlap:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
fatal = {"FATAL", "PANIC"}
seen = {"INFO", "FATAL"}
print(fatal.isdisjoint(seen))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
False

You could work this out with Chapter 7.4's intersection, and the two agree:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
fatal = {"FATAL", "PANIC"}
seen = {"INFO", "WARNING"}
print(fatal.isdisjoint(seen) == (len(fatal & seen) == 0))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
True

But isdisjoint says what you mean. len(a & b) == 0 makes the reader build a set, count it, compare the count to zero, and then work out what that implies — three steps to re-derive a question Python already names.

The empty set is disjoint from everything, for the same reason it is a subset of everything — there is no shared element because there is no element at all:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
print(set().isdisjoint({"a"}))
print(set().isdisjoint(set()))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
True
True

8. Neither Subset Nor Disjoint

Learners often assume two sets must be in one of these relationships. Most pairs are in none of them:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
x = {"a", "b"}
y = {"b", "c"}
print(x <= y)
print(y <= x)
print(x.isdisjoint(y))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
False
False
False

They share b, so they are not disjoint. Each has something the other lacks, so neither contains the other. This is the ordinary case — partial overlap — and it is exactly where Chapter 7.4's operations are the right tool: x & y tells you what they share, x - y what is unique to the first.

9. Operators and Methods

The same split as Chapter 7.4. Operators require sets on both sides:

Azvya Education Pvt. Ltd.VLSI Mentor
BROKEN ON PURPOSE
print({"a"} <= ["a"])
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
TypeError: '<=' not supported between instances of 'set' and 'list'

Methods accept any iterable:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
print({"a"}.issubset(["a", "b"]))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
True

A list on the right, and it worked. Useful when the other collection arrived as a list.

QuestionOperatorMethod
Is every element of A in B?A <= BA.issubset(B)
…and A is not equal to BA < B—
Does A contain every element of B?A >= BA.issuperset(B)
Do A and B share nothing?—A.isdisjoint(B)

10. Predict, Run, Observe

Predict every line before reading on. Two of these are the size trap and the direction trap.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
required = {"smoke", "reset"}
ran = {"smoke", "reset", "burst"}
print(required <= ran)
print(ran <= required)
print(required < ran)
print(required <= required)
print(required < required)
print({"z"} <= ran)
print(required.isdisjoint(ran))
print(set() <= required)

The verified output:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
True
False
True
True
False
False
False
True

Line 2 is False because the direction is reversed — burst ran without being required.

Line 3 is True: required is contained in ran and not equal to it, so it is a proper subset. Line 4 is True and line 5 is False — a set contains itself but is not a proper subset of itself.

Line 6 is the size trap: {"z"} is far smaller than ran and is not a subset, because "z" is not in it.

Line 7 is False — they share two elements, so they are certainly not disjoint. And line 8 is True, the empty set being a subset of everything.

11. Exercises

Exercise 1 — the coverage check. Given required = {"smoke", "reset", "crc"} and ran = {"smoke", "crc"}, write the line that answers "was everything required run?", predict the result, then write the line that shows what is missing.

Exercise 2 — predict. For a = {1, 2} and b = {1, 2, 3}, give each result and verify: (a) a <= b (b) b <= a (c) a < b (d) a <= a (e) a < a (f) b >= a (g) a.isdisjoint(b)

Exercise 3 — the size trap. Explain in three sentences why {"z"} <= {"a", "b", "c"} is False even though the left set is smaller, and state what subset actually tests.

Exercise 4 — the reversed check. A colleague checks coverage with ran <= required and reports a failure whenever an extra test runs. Say what their line actually asks, why it returns False, and give the corrected line.

Exercise 5 — pick the tool. For each, say whether you would use a 7.5 relationship test or a 7.4 operation, and write the expression: (a) did every required signal appear (b) which required signals did not appear (c) is there any fatal severity in the log at all (d) which severities appeared in both of two logs

Exercise 6 — disjoint two ways. Write fatal.isdisjoint(seen) and an equivalent expression using intersection. Say in two sentences why the first is the better line to submit for review.

Exercise 7 — the empty case. Explain in two sentences why set() <= anything is True, and what that means for a coverage check when nothing is required.

Exercise 8 — neither. Give two sets that are neither subsets of one another nor disjoint, prove all three tests are False, and say which chapter's tools you would reach for to describe their relationship.

12. Summary

  • These questions return booleans, where Chapter 7.4's operations returned sets. Use them when the answer is yes or no about the whole relationship.
  • A <= B asks whether every element of A is in B. A is a subset; B is a superset.
  • <= points at the bigger side. required <= ran is the coverage check. Reversing it asks a different, valid, misleading question.
  • A >= B is the mirror, and issubset / issuperset say it in words.
  • Subset is about membership, never size. {"z"} <= {"a", "b", "c"} is False despite being smaller.
  • A set is a subset of itself, so <= is not strict. < is the proper subset — contained and not equal.
  • The empty set is a subset of every set, which makes an empty requirement pass a coverage check with no special handling.
  • isdisjoint() asks whether two sets share nothing. Prefer it to testing whether an intersection is empty — it states the question.
  • The empty set is disjoint from everything.
  • Most pairs are neither subsets nor disjoint; partial overlap is the ordinary case and Chapter 7.4 describes it.
  • Operators need sets on both sides; methods accept any iterable.

One chapter remains, and it is the decision all six have been building towards.

Where this fits

Part of the Python curriculum.