CS Pathfinder Logo CS Pathfinder

Unit 5 · Advanced Topics

Sets in Python

Understand Python sets — unordered collections of unique elements. Learn how to create sets, use the set() constructor, work with frozensets, and apply sets for deduplication and membership testing.

Table of Contents

What Are Sets?

A set is an unordered collection of unique, immutable (hashable) elements. Sets are defined using curly braces {} or the set() constructor. They are similar to mathematical sets and support operations like union, intersection, and difference.

University Definition

A set in Python is a built-in data type that represents an unordered collection of unique, immutable elements. Sets are mutable (can add/remove elements) but do not allow duplicate values. They are implemented using a hash table, providing O(1) average-time complexity for membership testing.

Sets are particularly useful for:

  • Removing duplicates from a collection
  • Fast membership testing (checking if an element exists)
  • Mathematical set operations (union, intersection, difference)
  • Comparing collections of data

Creating Sets

# Creating sets using curly braces
fruits = {"apple", "banana", "cherry", "apple", "banana"}
print("Fruits:", fruits)
print("Type:", type(fruits))

# Duplicates are automatically removed
numbers = {1, 2, 3, 2, 1, 4, 3, 5}
print("Numbers:", numbers)

# Creating a set from a list (removes duplicates)
names_list = ["Rahul", "Priya", "Amit", "Rahul", "Priya"]
unique_names = set(names_list)
print("Unique names:", unique_names)

# Creating a set from a string
chars = set("programming")
print("Unique chars:", chars)

# Creating a set from a tuple
colors = set(("red", "blue", "green", "red"))
print("Colors:", colors)

# Empty set using set() constructor (NOT curly braces)
empty_set = set()
print("Empty set:", empty_set)
print("Type:", type(empty_set))

Output:

Fruits: {'banana', 'apple', 'cherry'}
Type: <class 'set'>
Numbers: {1, 2, 3, 4, 5}
Unique names: {'Rahul', 'Priya', 'Amit'}
Unique chars: {'p', 'r', 'o', 'g', 'a', 'm', 'i', 'n'}
Colors: {'red', 'blue', 'green'}
Empty set: set()
Type: <class 'set'>

Set Properties

Sets have several important properties that distinguish them from other data types:

1. Unordered

Elements in a set have no defined order and cannot be accessed by index.

fruits = {"apple", "banana", "cherry"}
print(fruits)
# You cannot do: fruits[0]  # TypeError!

# To access elements, iterate
for fruit in fruits:
    print(fruit)

# Convert to list for indexed access (order not guaranteed)
fruits_list = list(fruits)
print("As list:", fruits_list)

2. Unique Elements

Sets automatically remove duplicates — each element appears only once.

# Removing duplicates from a list
marks = [85, 92, 78, 92, 85, 76, 92, 85]
unique_marks = set(marks)
print("All marks:", marks)
print("Unique marks:", unique_marks)
print("Count of unique:", len(unique_marks))

# Practical: Finding common elements
class_a = {"Rahul", "Priya", "Amit", "Neha", "Rohan"}
class_b = {"Priya", "Rohan", "Sneha", "Karan", "Amit"}
common = class_a & class_b  # Intersection
print("Common students:", common)

3. Immutable Elements (Hashable)

Set elements must be immutable (strings, numbers, tuples). Lists and dicts cannot be set elements.

# Valid set elements (immutable/hashable)
valid_set = {1, "hello", (1, 2, 3), 3.14, True}
print("Valid set:", valid_set)

# Invalid - using mutable types causes TypeError
# invalid = {[1, 2, 3]}        # TypeError: unhashable type: 'list'
# invalid = {{"a": 1}}         # TypeError: unhashable type: 'dict'

# Using frozenset as a set element (since it's immutable)
nested = {frozenset([1, 2]), frozenset([3, 4])}
print("Nested frozensets:", nested)

# Tuples can be elements (they are immutable)
coord_set = {(0, 0), (1, 2), (3, 4), (1, 2)}
print("Coordinates:", coord_set)  # Duplicate (1,2) removed

4. Mutable (Can Be Modified)

While elements must be immutable, the set itself can be modified using add(), remove(), etc.

languages = {"Python", "Java"}
print("Before:", languages)

languages.add("C++")
print("After add:", languages)

languages.discard("Java")
print("After discard:", languages)

# Membership testing is very fast - O(1) average
print("Python in set:", "Python" in languages)     # True
print("Java in set:", "Java" in languages)         # False

Output (for all property examples):

apple
banana
cherry
As list: ['apple', 'banana', 'cherry']
All marks: [85, 92, 78, 92, 85, 76, 92, 85]
Unique marks: {76, 78, 85, 92}
Count of unique: 4
Common students: {'Amit', 'Priya', 'Rohan'}
Valid set: {True, 1, 3.14, 'hello', (1, 2, 3)}
Coordinates: {(0, 0), (1, 2), (3, 4)}
Before: {'Python', 'Java'}
After add: {'Python', 'Java', 'C++'}
After discard: {'Python', 'C++'}
Python in set: True
Java in set: False

set() Constructor

University Definition

The set() constructor creates a new set object. It can accept any iterable (list, tuple, string, dictionary) as an argument and returns a set with unique elements from that iterable. With no arguments, it creates an empty set.

# set() with no arguments - empty set
empty = set()
print("Empty set:", empty)

# set() from a list
from_list = set([1, 2, 3, 2, 1])
print("From list:", from_list)

# set() from a tuple
from_tuple = set(("a", "b", "a", "c"))
print("From tuple:", from_tuple)

# set() from a string
from_string = set("mississippi")
print("From string:", from_string)

# set() from a dictionary - uses keys only
from_dict = set({"name": "Rahul", "age": 20, "city": "Delhi"})
print("From dict:", from_dict)

# set() from range
from_range = set(range(1, 10))
print("From range:", from_range)

# set() from a generator expression
from_gen = set(x**2 for x in range(1, 6))
print("From generator:", from_gen)

# set() from a file (unique lines)
# with open("data.txt") as f:
#     unique_lines = set(f)

Output:

Empty set: set()
From list: {1, 2, 3}
From tuple: {'a', 'b', 'c'}
From string: {'m', 'i', 's', 'p'}
From dict: {'name', 'age', 'city'}
From range: {1, 2, 3, 4, 5, 6, 7, 8, 9}
From generator: {1, 4, 9, 16, 25}

Empty Set vs Empty Dict

Critical Distinction

Using {} creates an empty dictionary, NOT an empty set! To create an empty set, you must use set(). This is one of the most common mistakes in Python exams.

# This creates an EMPTY DICTIONARY, not a set!
empty_dict = {}
print("Type of {}:", type(empty_dict))  # <class 'dict'>

# This creates an EMPTY SET
empty_set = set()
print("Type of set():", type(empty_set))  # <class 'set'>

# Demonstrate the difference
empty_dict["key"] = "value"
print("Dict after assignment:", empty_dict)  # Works fine

# empty_set.add(1)  # Also works, but type matters!
empty_set.add(1)
print("Set after add:", empty_set)

# MCQ-style question
a = {}      # dict
b = set()   # set
print(f"a is dict: {isinstance(a, dict)}")     # True
print(f"b is set: {isinstance(b, set)}")       # True
print(f"b is dict: {isinstance(b, dict)}")     # False

Output:

Type of {}: <class 'dict'>
Type of set(): <class 'set'>
Dict after assignment: {'key': 'value'}
Set after add: {1}
a is dict: True
b is set: True
b is dict: False

frozenset

University Definition

A frozenset is an immutable version of a set. Once created, its elements cannot be added or removed. Frozensets can be used as elements of other sets or as dictionary keys because they are hashable.

# Creating frozensets
fs1 = frozenset([1, 2, 3, 4])
fs2 = frozenset([3, 4, 5, 6])
print("frozenset 1:", fs1)
print("Type:", type(fs1))

# Set operations work on frozensets (return frozensets)
print("Union:", fs1 | fs2)
print("Intersection:", fs1 & fs2)
print("Difference:", fs1 - fs2)

# Cannot modify a frozenset
# fs1.add(5)      # AttributeError: 'frozenset' has no attribute 'add'
# fs1.remove(1)   # AttributeError: 'frozenset' has no attribute 'remove'

# Using frozenset as dictionary key
permissions = {
    frozenset(["read", "write"]): "editor",
    frozenset(["read"]): "viewer",
    frozenset(["read", "write", "admin"]): "administrator"
}
user_perms = frozenset(["read", "write"])
print("Role:", permissions[user_perms])

# Using frozenset as element of a set (nested sets)
nested_sets = {frozenset([1, 2]), frozenset([3, 4]), frozenset([1, 2])}
print("Nested sets:", nested_sets)  # Duplicate frozenset removed

# Practical: Storing immutable configurations
configs = {
    frozenset(["verbose", "debug"]): {"level": "max"},
    frozenset(["quiet"]): {"level": "min"}
}

Output:

frozenset 1: frozenset({1, 2, 3, 4})
Type: <class 'frozenset'>
Union: frozenset({1, 2, 3, 4, 5, 6})
Intersection: frozenset({3, 4})
Difference: frozenset({1, 2})
Role: editor
Nested sets: {frozenset({1, 2}), frozenset({3, 4})}

University Exam Tip

frozenset vs set: A set is mutable (can add/remove elements), while a frozenset is immutable (cannot be modified after creation). Frozensets are hashable and can be dictionary keys or set elements. Regular sets are not hashable and cannot be nested. This is a common exam question.

When to Use Sets

# USE CASE 1: Remove duplicates from a list
data = [1, 2, 2, 3, 4, 4, 5, 1, 3]
unique_data = list(set(data))
print("Original:", data)
print("Without duplicates:", unique_data)

# USE CASE 2: Fast membership testing
large_list = list(range(1000000))
large_set = set(range(1000000))

import time
start = time.time()
999999 in large_list
print(f"List search: {time.time() - start:.6f}s")

start = time.time()
999999 in large_set
print(f"Set search: {time.time() - start:.6f}s")

# USE CASE 3: Find common/different elements
enrolled_math = {"Alice", "Bob", "Charlie", "Diana", "Eve"}
enrolled_physics = {"Bob", "Diana", "Frank", "Grace"}

both = enrolled_math & enrolled_physics
only_math = enrolled_math - enrolled_physics
only_physics = enrolled_physics - enrolled_math

print("Both subjects:", both)
print("Only Math:", only_math)
print("Only Physics:", only_physics)

# USE CASE 4: Data validation
valid_roles = {"admin", "editor", "viewer", "moderator"}
user_role = "admin"
if user_role in valid_roles:
    print(f"Valid role: {user_role}")
else:
    print("Invalid role!")

# USE CASE 5: Set difference for missing data
expected = {"A", "B", "C", "D", "E"}
submitted = {"A", "C", "E"}
missing = expected - submitted
print("Missing submissions:", missing)

Output:

Original: [1, 2, 2, 3, 4, 4, 5, 1, 3]
Without duplicates: [1, 2, 3, 4, 5]
List search: 0.031250s
Set search: 0.000001s
Both subjects: {'Diana', 'Bob'}
Only Math: {'Alice', 'Charlie', 'Eve'}
Only Physics: {'Frank', 'Grace'}
Valid role: admin
Missing submissions: {'B', 'D'}

Key Points

Sets are unordered collections of unique, immutable (hashable) elements.

Use {} with values for sets, set() for empty sets — {} creates a dict.

Elements must be hashable (strings, numbers, tuples) — no lists or dicts inside sets.

Membership testing (in) is O(1) average — much faster than lists.

frozenset is an immutable set — can be used as dict keys or set elements.

Sets are ideal for deduplication, membership checks, and mathematical operations.

Use set() constructor to convert lists, tuples, strings, and dict keys to sets.

Sets maintain insertion order in Python 3.7+ (implementation detail, not guaranteed).

Common Mistakes to Avoid

  • Using {} instead of set() for empty sets (creates a dict instead).
  • Trying to access elements by index — sets are unordered and do not support indexing.
  • Trying to add mutable objects (lists, dicts) as set elements — raises TypeError.
  • Expecting sets to maintain a specific order — they are unordered by design.
  • Confusing remove() (raises KeyError if missing) with discard() (no error).

Practice Questions

  1. Write a program to find all unique words in a sentence using sets.
  2. Create a frozenset and demonstrate why it can be used as a dictionary key but a regular set cannot.
  3. Given two lists of student IDs, find students who are in both lists, only in the first, and only in the second.
  4. Write a function that takes a list and returns a list with duplicates removed while preserving order (hint: use a set for tracking).
  5. Demonstrate the performance difference between in operator on a list vs a set with timing.

Summary

Python sets are powerful data structures for storing unique, hashable elements. They provide O(1) membership testing, automatic deduplication, and support mathematical set operations. The set() constructor converts iterables to sets, while frozenset creates immutable versions. Remember that {} creates a dict, not an empty set — always use set() for empty sets.

Python Programming Handwritten Notes

Master Python Programming with Easy Handwritten Notes – Perfect for Interviews, Placements, GATE & Exams.