Python tutorials  /  Python Sets - Complete Guide with Examples
Chapter 10 · Python

Python Sets - Complete Guide with Examples

A set in Python is an unordered collection of unique, immutable elements. Sets are defined using curly braces {} or the set() constructor, and they automatically eliminate duplicate values while providing highly efficient membership testing and mathematical set operations like union, intersection, and difference.

Think of a set like a guest list at an exclusive event where no name can appear twice, no matter how many times someone tries to add it. There is no numbered seating (no order or index), but the bouncer at the door can instantly tell you whether a specific name is on the list without checking every single entry one by one.

Consider CompileX's tagging system for coding problems, where each problem can be tagged with topics like 'arrays', 'recursion', or 'dynamic-programming'. When a user filters problems by selecting multiple tags such as 'arrays' and 'sorting', the platform needs to instantly find all problems that contain both tags. By storing each problem's tags as a Python set, the platform can use set intersection to find common tags between the user's selection and each problem's tag set in microseconds, even across tens of thousands of problems, which would be significantly slower if tags were stored as lists requiring repeated linear scans.

Sets are essential because they provide extremely fast membership testing (checking if an item exists), automatically remove duplicate entries without extra code, and offer built-in mathematical operations for comparing collections, such as finding common elements, unique elements, or differences between two groups of data. This makes them ideal for deduplication, filtering, and relationship analysis tasks that would otherwise require slow, manual loops with lists.

  • Mutable Set (set): The standard, most commonly used set type in Python, created with {} or set(), which allows adding and removing elements after creation using methods like add() and remove().
  • Immutable Set (frozenset): A read-only version of a set created using frozenset(), which cannot be modified after creation, making it hashable and usable as a dictionary key or as an element within another set.
  • Empty Set: A set with no elements, which must be created using set() rather than {}, since {} creates an empty dictionary instead of an empty set in Python.
# Creating sets my_set = {element1, element2, element3} my_set = set(iterable) empty_set = set() # Common operations my_set.add(item) my_set.remove(item) my_set.discard(item) set_a | set_b # union set_a & set_b # intersection set_a - set_b # difference set_a ^ set_b # symmetric difference item in my_set # membership test
Imagine you are building an analytics feature for CompileX that compares two groups of students: those who attempted 'Problem A' and those who attempted 'Problem B'. You need to quickly find students who attempted both problems, students who attempted only one of the two, and remove any accidental duplicate student ID entries from the logs. Using lists for this would require slow, nested loops with O(n*m) complexity. How do you perform these comparisons efficiently and automatically avoid duplicate entries?
Creating a Set and Removing Duplicates
Demonstrates how converting a list with duplicate values into a set automatically removes all repeated entries.
Python
student_ids = [101, 102, 103, 101, 104, 102, 105] unique_ids = set(student_ids) print(unique_ids) print(len(unique_ids))
{101, 102, 103, 104, 105} 5
Passing the list to set() automatically discards duplicate values, keeping only one copy of each unique student ID. Note that the printed order of elements may vary since sets are unordered.
Fast Membership Testing
Shows how checking whether an item exists in a set is significantly more direct than searching through a list, useful for validating banned usernames.
Python
banned_usernames = {"admin", "root", "test", "null"} username = "admin" if username in banned_usernames: print(f"'{username}' is not allowed.") else: print(f"'{username}' is available.")
'admin' is not allowed.
The 'in' operator on a set uses hashing internally, giving average O(1) constant-time lookups regardless of how many banned usernames exist, making it ideal for large blocklists.
Set Operations: Union, Intersection, and Difference
Solves the analytics problem by comparing two groups of students who attempted different problems using set operations.
Python
attempted_problem_a = {"Aditi", "Rohan", "Meera", "Karan"} attempted_problem_b = {"Meera", "Karan", "Priya", "Sanya"} both = attempted_problem_a & attempted_problem_b only_a = attempted_problem_a - attempted_problem_b any_problem = attempted_problem_a | attempted_problem_b print(f"Attempted both: {both}") print(f"Only Problem A: {only_a}") print(f"Attempted at least one: {any_problem}")
Attempted both: {'Meera', 'Karan'} Only Problem A: {'Aditi', 'Rohan'} Attempted at least one: {'Aditi', 'Rohan', 'Meera', 'Karan', 'Priya', 'Sanya'}
The '&' operator returns elements present in both sets (intersection), '-' returns elements present only in the left set but not the right (difference), and '|' returns all unique elements from both sets combined (union). These operations run efficiently without manual looping.
Using frozenset as a Dictionary Key
Demonstrates using an immutable frozenset to represent a fixed combination of tags that can be safely used as a dictionary key.
Python
tag_combo = frozenset({"arrays", "sorting"}) problem_cache = {tag_combo: ["Problem 12", "Problem 45"]} print(problem_cache[frozenset({"sorting", "arrays"})])
['Problem 12', 'Problem 45']
Because frozenset is immutable and hashable, it can be used as a dictionary key. Since sets are unordered, frozenset({"arrays", "sorting"}) and frozenset({"sorting", "arrays"}) are considered equal and produce the same hash, correctly retrieving the cached value.
  • Using {} to Create an Empty Set: Writing 'my_set = {}' actually creates an empty dictionary, not an empty set, because curly braces default to dictionary syntax in Python. To create a truly empty set, developers must explicitly use 'my_set = set()'.
  • Assuming Sets Preserve Insertion Order: Since sets are unordered collections, relying on the order elements were added (like expecting the first added item to print first) leads to unpredictable and inconsistent behavior across different runs or Python versions, unlike lists which strictly preserve order.
  • Trying to Add Mutable Objects Like Lists to a Set: Attempting to add a list to a set, such as 'my_set.add([1, 2, 3])', raises a TypeError because lists are mutable and unhashable. Only immutable, hashable objects like strings, numbers, tuples, or frozensets can be stored as set elements.
  • Using remove() Instead of discard() for Uncertain Elements: Calling .remove(item) on a set when the item might not exist raises a KeyError and crashes the program. Using .discard(item) instead silently does nothing if the item is absent, which is safer when the element's presence is not guaranteed.
  • Confusing Set Difference Direction: Forgetting that 'set_a - set_b' is not the same as 'set_b - set_a' leads to incorrect results, since set difference is not commutative; the order of operands determines which set's exclusive elements are returned.
  • Use Sets for Fast Membership Testing Over Lists: When your code frequently checks whether an item exists in a collection (e.g., 'if x in collection'), and duplicates and order don't matter, use a set instead of a list to gain significant performance improvements, especially with large datasets.
  • Use Set Operations Instead of Manual Loops for Comparisons: When comparing two collections to find common, unique, or differing elements, use built-in set operations like &, |, -, and ^ instead of writing nested for loops, resulting in cleaner, more readable, and significantly faster code.
  • Use frozenset for Immutable, Hashable Collections: When you need a fixed collection of unique items that will never change and might be used as a dictionary key or stored inside another set, use frozenset() instead of a regular mutable set.
  • Prefer discard() Over remove() for Safer Deletions: Use .discard() when there's a chance the element being removed may not exist in the set, to avoid unexpected KeyError exceptions and the need for extra try-except blocks.
  • Convert to a List or Sorted List Only When Order Matters for Output: Since sets don't guarantee order, if you need to display set contents in a predictable, consistent sequence (like alphabetically), explicitly convert the set using sorted(my_set) before presenting it to users or writing it to a report.
What is the main difference between a list and a set in Python?
A list is an ordered collection that allows duplicate elements and maintains the sequence in which items were added, accessible via index. A set is an unordered collection that automatically enforces uniqueness, removing any duplicate values, and does not support indexing since there is no fixed order to its elements.
Why can't you create an empty set using {}?
In Python, curly braces {} are reserved for creating an empty dictionary by default, since dictionaries were introduced with this syntax first and it remains the default interpretation. To explicitly create an empty set, you must use the set() constructor instead, i.e., 'empty_set = set()'.
What is the time complexity of checking membership ('in') in a set compared to a list, and why?
Checking membership in a set has an average time complexity of O(1) because sets are implemented using hash tables, where each element's hash value determines its storage location, allowing near-instant lookups. Checking membership in a list has a time complexity of O(n) because Python must potentially scan through every element sequentially until a match is found or the list ends.
Why must elements added to a set be hashable, and what does that mean?
Sets are implemented internally using hash tables, where each element's position is determined by computing a hash value based on its content. Hashable objects, like strings, numbers, and tuples, produce a consistent hash value throughout their lifetime because they are immutable. Mutable objects like lists or dictionaries cannot be hashed because their content (and thus their hash) could change after insertion, which would break the internal structure of the set, so Python disallows adding them directly.
How does a frozenset differ from a regular set, and when would you use one?
A frozenset is an immutable version of a set; once created, elements cannot be added or removed from it, unlike a regular mutable set. Because of this immutability, a frozenset is hashable and can be used as a dictionary key or as an element within another set, which a regular set cannot do since regular sets themselves are unhashable due to their mutability. Frozensets are useful when you need a fixed, constant collection of unique values that should never change, such as representing a permanent category of allowed values.
Write a function 'has_unique_chars' that takes a string and returns True if all characters in it are unique (no repeats), using a set.
def has_unique_chars(s): return len(set(s)) == len(s) print(has_unique_chars("python")) # True print(has_unique_chars("hello")) # False
Write a function 'common_elements' that takes two lists and returns a set of elements that appear in both lists.
def common_elements(list1, list2): return set(list1) & set(list2) print(common_elements([1, 2, 3, 4], [3, 4, 5, 6])) # {3, 4}
Write a function 'symmetric_difference_report' that takes two sets representing students who attempted two different problems, and returns a set of students who attempted exactly one of the two problems (not both).
def symmetric_difference_report(set_a, set_b): return set_a ^ set_b group_a = {"Aditi", "Rohan", "Meera"} group_b = {"Meera", "Karan", "Priya"} print(symmetric_difference_report(group_a, group_b)) # {'Aditi', 'Rohan', 'Karan', 'Priya'}

Python sets are unordered collections of unique, hashable elements that provide extremely efficient membership testing and built-in mathematical operations like union, intersection, difference, and symmetric difference. They are invaluable for deduplication, fast lookups, and comparing relationships between groups of data, offering significant performance advantages over lists for these specific use cases. Understanding the distinction between mutable sets and immutable frozensets, along with proper set operation semantics, is essential for writing efficient, production-quality Python code.

© 2026 CompileX. Maintained by Aditya Kumar Sharma.