Python Sets: Unique, Unordered Collections
Learn how Python sets store unique values, remove duplicates, test membership, change contents, and perform union, intersection, difference, and other set operations.
A set is a Python collection designed to store distinct, or unique, values. Sets are useful when duplicate entries should be eliminated or when your main question is whether a value exists in a collection.
Unlike a list, which is a sequence with positions, a set is primarily used for membership tests. A membership test checks whether a value exists, usually with in or not in.
Creating a Set
A non-empty set can be written with curly braces. This syntax is called a set literal.
programming_languages = {'Python', 'JavaScript', 'Go'}
print(programming_languages)
The displayed order is not meaningful. Sets are unordered: they do not provide a stable positional order that your program should depend on.
An empty set must be created with set():
names = set()
print(names) # set()
Although curly braces create a non-empty set, {} creates an empty dictionary:
empty_dictionary = {}
empty_set = set()
You can also create a set from any iterable, such as a list, string, tuple, or another set:
course_tags = ['python', 'beginner', 'python', 'collections']
unique_tags = set(course_tags)
print(unique_tags)
print(len(unique_tags))
How Sets Remove Duplicates
When a set is created, repeated equal values are stored only once. The duplicates are discarded during creation.
scores = {10, 20, 10, 30, 20}
print(scores) # {10, 20, 30} in some display order
print(len(scores)) # 3
The same rule applies when adding values later. Calling add() with a value already present does not create a second copy.
attendees = {'Maya', 'Noah'}
attendees.add('Maya')
print(attendees) # Maya still appears only once
Duplicate removal is based on equality and hashing. A value is hashable when it can provide a stable hash value and can therefore be used as a set element or dictionary key.
Sets Are Unordered
A set does not preserve a defined item position or insertion order for programming purposes. Its printed or iterated order can differ between runs, Python versions, or environments.
colors = {'red', 'green', 'blue'}
print(colors) # Do not rely on the displayed order
Sets cannot be indexed or sliced:
colors = {'red', 'green', 'blue'}
# colors[0] # TypeError
# colors[0:2] # TypeError
Use a membership test or loop when you need to work with set values. If you need a predictable ordered view, convert the set to a sorted list:
ordered_colors = sorted(colors)
print(ordered_colors)
Mutable Sets and Hashable Elements
A normal set is a mutable container. You can add and remove elements after creating it. However, every direct element inside the set must be hashable.
Immutable values such as strings, integers, booleans, and tuples containing hashable values can normally be set elements. Mutable values such as lists and dictionaries cannot be direct elements:
valid_values = {1, 'Python', (10, 20)}
# invalid_values = {[1, 2], {'language': 'Python'}}
# TypeError: unhashable type
Use a tuple when fixed sequence data should be an element, or a frozenset when nested set-like data is appropriate:
coordinates = {(10, 20), (30, 40)}
permissions = {frozenset({'read', 'write'})}
A frozenset is an immutable set variant. It cannot be changed after creation and can itself be used as a set element or dictionary key.
| Feature | set | frozenset |
|---|---|---|
| Can add or remove elements | Yes | No |
| Can be used as a set element | No | Yes |
| Can be used as a dictionary key | No | Yes |
| Creation syntax | {1, 2} or set(iterable) | frozenset(iterable) |
Basic Set Operations
Adding Values
Use add() to add one element. If the element is already present, the set remains unchanged.
names = {'Maria', 'Lucy'}
names.add('Nina')
names.add('Maria')
print(names)
Use update() to add several elements from another iterable:
languages = {'Python'}
languages.update(['JavaScript', 'Go', 'Python'])
print(languages)
Removing Values
remove(value) removes a known value, but raises KeyError if that value is absent. discard(value) removes a value if present and does nothing if it is absent.
languages = {'Python', 'JavaScript', 'Go'}
languages.remove('Go')
languages.discard('Ruby') # Safe when Ruby is absent
# languages.remove('Ruby') # KeyError
Use pop() to remove and return an arbitrary element. Because sets are unordered, you cannot predict which element it returns. Calling it on an empty set raises KeyError.
languages = {'Python', 'JavaScript'}
removed_language = languages.pop()
print(removed_language)
Use clear() to remove every element:
languages.clear()
print(languages) # set()
Membership and Size
Use in and not in to test membership. Use len() to count the unique elements.
approved_usernames = {'maya', 'noah', 'li'}
username = 'maya'
if username in approved_usernames:
print('Access approved')
if 'admin' not in approved_usernames:
print('Admin is not approved')
print(len(approved_usernames)) # 3
| Operation | Method or operator | Result | Important behavior |
|---|---|---|---|
| Add one element | values.add(item) | Changes the set | Existing duplicates are ignored |
| Add multiple elements | values.update(iterable) | Changes the set | Adds each distinct element from the iterable |
| Safe removal | values.discard(item) | Changes the set if present | No error when absent |
| Strict removal | values.remove(item) | Changes the set | Raises KeyError when absent |
| Membership check | item in values | True or False | Useful for presence checks |
| Union | values.union(other) or values | other | All distinct elements | Elements from either set |
| Intersection | values.intersection(other) or values & other | Shared elements | Elements in both sets |
| Difference | values.difference(other) or values - other | Elements only in the first set | Order matters between the operands |
| Symmetric difference | values.symmetric_difference(other) or values ^ other | Elements in exactly one set | Shared elements are excluded |
| Count elements | len(values) | Number of unique elements | Duplicates are never counted twice |
Set Algebra
Set algebra compares collections of values. The named methods make the operation explicit, while operators provide concise notation.
coding_club = {'Maya', 'Noah', 'Li'}
robotics_club = {'Noah', 'Ava', 'Li'}
all_members = coding_club | robotics_club
shared_members = coding_club & robotics_club
coding_only = coding_club - robotics_club
exactly_one_club = coding_club ^ robotics_club
print(all_members)
print(shared_members)
print(coding_only)
print(exactly_one_club)
- Union: values present in either set. Use
union()or|. - Intersection: values shared by both sets. Use
intersection()or&. - Difference: values in one set but absent from another. Use
difference()or-. - Symmetric difference: values present in exactly one of two sets, not both. Use
symmetric_difference()or^.
For example, coding_club - robotics_club means members only in the coding club. Reversing the operands produces members only in the robotics club.
Choosing a Set, List, or Dictionary
| Collection type | Stores | Duplicates allowed | Order or position | Indexing | Typical use |
|---|---|---|---|---|---|
| set | Distinct hashable values | No | No stable positional order | No | Uniqueness, membership tests, set algebra |
| list | Values in a sequence | Yes | Preserves sequence order | Yes | Ordered data, repeated values, positional access |
| dictionary | Unique keys mapped to values | Keys must be unique | Preserves insertion order | No numeric indexing | Looking up a value by a named key |
Choose a set when uniqueness and efficient membership checks matter. Choose a list when order, indexing, or duplicate counts matter. Choose a dictionary when each key needs an associated value.
For example, a list can represent every vote, including repeated votes, while a set can represent the distinct usernames that submitted votes. A dictionary can map each username to that user's vote count.
Common Problems and Fixes
{}is not an empty set: useset(), because{}is an empty dictionary.- Indexing fails: sets have no positional indexing. Use
value in values, iterate over the set, or usesorted(values)for an ordered view. - Printed order changes: do not depend on set display or iteration order. Sort a copy when predictable presentation is required.
TypeErrorwhen adding a list or dictionary: those mutable objects are unhashable. Use a tuple for fixed sequence data or afrozensetfor nested set-like data.remove()raisesKeyError: usediscard()when absence is acceptable, or test membership before callingremove().- Confusing element immutability with container immutability: elements must be hashable, but a normal set can still change with
add(),update(), and removal methods. Usefrozensetwhen the collection itself must not change.
Quick Practice Example
names = set(['Maria', 'Lucy', 'Maria', 'Tanya'])
print(names)
print(len(names))
names.add('Nina')
names.update(['Lucy', 'Omar'])
names.discard('Lucy')
print('Maria' in names)
print('Lucy' not in names)
print(names)
This example converts a list to a set, removes the repeated 'Maria', adds one value, adds several values, safely removes a value, and performs membership tests.
Summary
- A set stores distinct hashable elements.
- Duplicate equal values are discarded during creation and later additions.
- Sets are unordered and cannot be indexed or sliced.
- Normal sets are mutable;
frozensetis immutable. - Use
in,not in, andlen()for common membership and size operations. - Use union, intersection, difference, and symmetric difference to compare groups of values.
- Use lists for ordered sequences, sets for uniqueness, and dictionaries for key-value mappings.
For related collection concepts, review Python lists, modifying lists, and looping through dictionaries. You can also explore the Python online course.