Sets in Python
A set (set) in Python solves two specific tasks: fast membership testing (is an element in the collection?) and storing only unique values without duplicates. It's an unordered collection based on the mathematical concept of a set.
What is a set?
A set in Python is an unordered collection of unique elements. Two key properties of sets:
- Unordered: elements have no specific order and are not indexed — you can't reach for "the third element of a set", only iterate over all of them or check whether a particular one is present
- Unique: each element appears only once
Main characteristics of sets:
- Mutability: you can add and remove elements
- Immutable elements: only immutable objects can go inside a set (numbers, strings, tuples)
- Efficiency: optimized for fast membership testing
Because sets are based on the mathematical concept, they support union, intersection, and difference operations.
Creating sets
Using curly braces
Python 3.13# Set of integers numbers = {1, 2, 3, 4, 5} print(numbers){1, 2, 3, 4, 5}# Automatic duplicate removal duplicates = {1, 2, 2, 3, 3, 3, 4, 5, 5} print(duplicates){1, 2, 3, 4, 5}# A single set can hold different immutable types mixed = {1, "hello", (1, 2, 3)} print(len(mixed)) # number, string and tuple — all three fit3
Using the set() constructor
Python 3.13# Empty set empty_set = set() print(empty_set)set()# Creating a set from a list numbers_set = set([1, 2, 2, 3, 4, 4, 5]) print(numbers_set){1, 2, 3, 4, 5}# Creating a set from a string — repeated letters collapse letters = set("hello") print(len(letters)) # "hello" has two 'l', the set keeps one — 4 letters total4
Basic operations with sets
Checking for element presence
Python 3.13fruits = {"apple", "banana", "cherry"} print("apple" in fruits)Trueprint("pear" in fruits)False
Adding and removing elements
The order of elements in a set is arbitrary, so in the string examples below we print them via sorted(), which returns a sorted list — that keeps the output from jumping around between runs.
Python 3.13fruits = {"apple", "banana"} # Adding a single element fruits.add("cherry") print(sorted(fruits))['apple', 'banana', 'cherry']# Adding multiple elements fruits.update(["pear", "orange"]) print(sorted(fruits))['apple', 'banana', 'cherry', 'orange', 'pear']# Removing an element fruits.remove("banana") # raises KeyError if element doesn't exist print(sorted(fruits))['apple', 'cherry', 'orange', 'pear']# Safely removing an element fruits.discard("cherry") # doesn't raise an error if element doesn't exist print(sorted(fruits))['apple', 'orange', 'pear']# pop() removes and returns some element — which one exactly is not known in advance removed = fruits.pop() print(len(fruits)) # one fewer than before2# Clearing the set fruits.clear() print(fruits)set()
Looping over a set
You go through a set with a for loop. The order is arbitrary and may change from run to run — that's what "unordered" means. When you need a predictable order, sort with sorted():
Python 3.13colors = {"red", "blue", "green"} for color in sorted(colors): print(color)blue green red
Mathematical set operations
Three main operations: union, intersection, and difference. They're easy to visualise with Venn diagrams:

Union
All elements from both sets:
Python 3.13a = {1, 2, 3} b = {3, 4, 5} union_set = a | b print(union_set){1, 2, 3, 4, 5}
The same can be written as a.union(b).
Intersection
Elements that are in both sets:
Python 3.13a = {1, 2, 3, 4} b = {3, 4, 5, 6} intersection_set = a & b print(intersection_set){3, 4}
The same can be written as a.intersection(b).
Difference
Elements from the first set that are not in the second:
Python 3.13a = {1, 2, 3, 4} b = {3, 4, 5, 6} difference_set = a - b print(difference_set){1, 2}
The same can be written as a.difference(b).
Comparing sets
Python 3.13a = {1, 2, 3} b = {1, 2, 3, 4, 5} c = {1, 2, 3} # Set equality print(a == c) # Contains the same elementsTrue# Subsets print(a.issubset(b)) # All elements of a are in bTrueprint(a < b) # a is a proper subset of bTrue# Supersets print(b.issuperset(a)) # b contains all elements of aTrueprint(b > a) # b is a proper superset of aTrue# Checking for no common elements d = {6, 7, 8} print(a.isdisjoint(d)) # No common elementsTrue
Immutable sets (frozenset)
If you need an immutable version of a set, use frozenset:
Python 3.13# Creating a frozenset immutable_set = frozenset([1, 2, 3, 4]) print(immutable_set)frozenset({1, 2, 3, 4})# Attempting to modify a frozenset raises an error try: immutable_set.add(5) except AttributeError as e: print(f"Error: {e}")Error: 'frozenset' object has no attribute 'add'# frozenset can be used as a dictionary key or an element of another set normal_set = {frozenset([1, 2]), frozenset([3, 4])} print(len(normal_set)) # both frozensets fit inside2
Practical examples of using sets
1. Removing duplicates from a list
Python 3.13numbers = [1, 2, 2, 3, 3, 3, 4, 5, 5] unique_numbers = list(set(numbers)) print(unique_numbers)[1, 2, 3, 4, 5]
2. Finding common elements
Python 3.13users_group1 = ["Anna", "Ivan", "Maria", "Peter", "Elena"] users_group2 = ["Ivan", "Olga", "Elena", "Alex"] # Common elements (intersection) common_users = set(users_group1) & set(users_group2) print(f"Users in both groups: {sorted(common_users)}")Users in both groups: ['Elena', 'Ivan']# Elements only in the first group (difference) only_group1 = set(users_group1) - set(users_group2) print(f"Only in group 1: {sorted(only_group1)}")Only in group 1: ['Anna', 'Maria', 'Peter']# All unique elements (union) all_users = set(users_group1) | set(users_group2) print(f"All unique users: {sorted(all_users)}")All unique users: ['Alex', 'Anna', 'Elena', 'Ivan', 'Maria', 'Olga', 'Peter']
3. Checking for uniqueness of elements
Python 3.13def are_all_unique(items): """Checks if all elements in a sequence are unique.""" return len(set(items)) == len(items) print(are_all_unique([1, 2, 3, 4, 5]))Trueprint(are_all_unique([1, 2, 3, 3, 4]))False
Limitations and performance
Limitations
Set elements must be hashable (immutable):
Python 3.13# Works with immutable data types valid_set = {1, "hello", (1, 2, 3)} print(len(valid_set)) # number, string and tuple are all hashable — all three fit3# Error with mutable data types try: invalid_set = {1, [2, 3], {"a": 1}} except TypeError as e: print(f"Error: {e}")Error: unhashable type: 'list'
You can add:
- Numbers (int, float, complex)
- Strings (str)
- Tuples (tuple) with hashable elements
- Frozenset
You cannot add:
- Lists (list)
- Dictionaries (dict)
- Sets (set)
Performance
Fast lookup is exactly what sets are built for. Let's test on a million numbers: we look for the last one — the worst case for a list, which has to scan through everything.
Python 3.13import time data = list(range(1_000_000)) data_set = set(data) start = time.time() for _ in range(100): 999_999 in data list_time = time.time() - start start = time.time() for _ in range(100): 999_999 in data_set set_time = time.time() - start print(f"Search in list: {list_time:.3f} sec")Search in list: 0.442 secprint(f"Search in set: {set_time:.5f} sec")Search in set: 0.00001 sec
Your exact numbers will differ — they depend on the machine and how busy it is — but the gap stays just as wide: tens of thousands of times. The list has to check elements one by one until it finds the right one. Instead of scanning, a set computes straight away where the value should sit and checks only that spot — and it does so equally fast whether there are ten elements or a million.
Operations with O(1) complexity (constant time):
- Testing for membership: x in set
- Adding an element: set.add(x)
- Removing an element: set.remove(x), set.discard(x)
Check your understanding
What does print(set([1, 2, 2, 3, 3, 3])) output?
