Sets in Python: A Complete Tutorial with Worked Examples

Learn how Python sets store unique values, how each core operation works, and how to avoid the errors that catch beginners, with every output worked through.

KnowledgeGate Team

Exam prep & CS education

Updated 23 Aug 20266 min read

Most beginners meet Python sets and think, "It is just a list without duplicates." Then s[0] throws an error, or {} turns out to be a dictionary. The real challenge is knowing when to choose a set, what each operation returns, and why membership checks are fast. Choose a set for fast membership checks, deduplication, and mathematical set operations, not for positional access.

What a set is and how to create one

A set is an unordered collection of unique, hashable items. "Unique" means duplicate values collapse into one. "Unordered" means there is no fixed position for an item, so a set does not support indexing.

Start with a set literal:

s = {3, 1, 4, 1, 5, 9, 2, 6}
print(s)
print(len(s))

The set conceptually contains {1, 2, 3, 4, 5, 6, 9}, though Python does not promise that display order. Eight items were written, but 1 appears twice and is stored once. Therefore, len(s) is 7.

You can also build a set from any suitable iterable:

set([1, 1, 2, 3, 3, 3])  # {1, 2, 3}
set("hello")              # contains {'h', 'e', 'l', 'o'}

The first result has three values. The second has four characters because the two occurrences of l become one element. To create an empty set, write set(). The expression {} creates an empty dictionary.

Adding and removing elements

Sets are mutable, so you can add and remove elements after creation. Follow one set through the main methods:

nums = {1, 2, 3}
nums.add(4)             # {1, 2, 3, 4}
nums.add(2)             # unchanged: {1, 2, 3, 4}
nums.update([4, 5, 6])  # {1, 2, 3, 4, 5, 6}
nums.discard(10)        # unchanged, no error
nums.remove(10)         # raises KeyError

add accepts one item. update accepts an iterable and adds each of its items. Adding 2 again or updating with the existing 4 does not create duplicates.

Method

What it does

Error if the requested item is missing?

add(x)

Adds one item

No

update(items)

Adds every item from an iterable

No

remove(x)

Removes a particular item

Yes, KeyError

discard(x)

Removes a particular item if present

No

pop()

Removes and returns one arbitrary item

Yes, if the set is empty

Because a set has no fixed order, pop() does not mean "remove the last item." Never write logic that depends on which element it returns.

Four core set operations with worked results

Fix two sets and reuse them:

A = {1, 2, 3, 4, 5}
B = {4, 5, 6, 7, 8}

The four core operations are:

  • Union: A | B, or A.union(B), gives {1, 2, 3, 4, 5, 6, 7, 8}. It keeps everything found in either set.

  • Intersection: A & B, or A.intersection(B), gives {4, 5}. It keeps only values found in both.

  • Difference: A - B, or A.difference(B), gives {1, 2, 3}. In the other direction, B - A gives {6, 7, 8}. Difference is not symmetric.

  • Symmetric difference: A ^ B, or A.symmetric_difference(B), gives {1, 2, 3, 6, 7, 8}. It keeps values found in exactly one set, not both.

The counts provide a useful check. Here, |A| = 5, |B| = 5, and |A & B| = 2. The union must therefore contain 5 + 5 - 2 = 8 elements, which matches the eight values above.

A Venn diagram of sets A and B: 1, 2, 3 sit in A only, 4 and 5 in the overlap, and 6, 7, 8 in B only.

Subsets, supersets and comparisons

Set comparisons test relationships and return True or False. They do not produce a new set. Let X = {1, 2} and Y = {1, 2, 3, 4}.

X <= Y                 # True, X is a subset of Y
X.issubset(Y)          # True
Y >= X                 # True, Y is a superset of X
Y.issuperset(X)        # True
X < Y                  # True, X is a proper subset of Y
Y <= Y                 # True
Y < Y                  # False

A set is a subset of itself, but it cannot be a proper subset of itself. Disjoint sets have no shared elements: {1, 2}.isdisjoint({3, 4}) is True, while {1, 2}.isdisjoint({2, 3}) is False because both contain 2.

Order does not affect equality. {1, 2, 3} == {3, 2, 1} evaluates to True.

Why sets are fast: membership and comprehensions

The practical reason to choose a set is fast membership testing. A list lookup is O(n) because Python may need to scan its items one by one. A set stores items by hash, so membership is O(1) on average. Worst-case performance can degrade, but that is rare in ordinary use.

For example, checking 999999 in a_list when the list has one million items may inspect up to one million entries. With 999999 in a_set, Python uses the value's hash to jump towards its bucket.

Sets also make deduplication concise:

unique = list(set([4, 4, 2, 7, 2, 9]))

unique contains the four distinct values 2, 4, 7, and 9, but their list order is not guaranteed. A set comprehension builds a set while applying an expression:

{x * x for x in range(1, 6)}  # {1, 4, 9, 16, 25}

Those are the squares of 1, 2, 3, 4, and 5. A frozenset([1, 2, 3]) is the immutable version. Unlike a regular set, it can be used as a dictionary key or placed inside another set.

A comparison of list membership scanning items one by one (O(n)) against set membership jumping straight to a value by its hash (O(1)).

Traps that catch beginners

These mistakes cause most wrong outputs and exceptions:

  • Using {} for an empty set: it creates a dictionary. Use set().

  • Trying an index: s[0] raises TypeError: 'set' object is not subscriptable. Iterate over the set, or convert it to a list when you genuinely need indexing.

  • Adding an unhashable item: {[1, 2], 3} raises TypeError: unhashable type: 'list'. Use a tuple such as (1, 2), or use a frozenset when the item itself represents a set.

  • Removing an absent value: remove raises KeyError if the item is missing. Use discard when absence is acceptable.

  • Expecting duplicate counts: len({1, 1, 1}) is 1, not 3. Use collections.Counter when you need the frequency of each value.

How the exam tests sets

Exam and placement questions usually ask you to predict output, evaluate a chain of operations, or identify the line that raises an exception. Consider:

({1, 2, 3, 4, 5} & {4, 5, 6}) | {7}

The intersection is evaluated first and gives {4, 5}. Its union with {7} is {4, 5, 7}. Another common pattern is len(set("mississippi")). The unique letters are m, i, s, and p, so the answer is 4.

Practise these patterns with the Data Structures MCQs collection and Data Structures Graphs MCQs. Sets also appear in real algorithms, including visited-node tracking in graph traversal and duplicate detection. The Python tutorial hub connects these operations to related lessons.

The short version and next step

A set is unordered, stores unique hashable values, and is mutable. Use frozenset when you need an immutable set. Remember | for union, & for intersection, - for difference, and ^ for symmetric difference. Choose a set for fast membership and deduplication, not for ordered or indexed data.

For a structured path from Python basics through built-in types, continue with the Python Programming course. To apply sets in visited-node tracking, deduplication, hashing, and other algorithmic patterns, work through DSA Using Python.