Skip to main content

Sets in Python: Unique Items and Operations

Sets are Python's answer to mathematical sets: unordered collections that automatically enforce uniqueness, making them ideal for removing duplicates and performing membership tests. Unlike lists and tuples, sets cannot contain duplicate items, and unlike dictionaries, they store values without keys. The four core set operations—union, intersection, difference, and symmetric difference—enable efficient comparison of collections.

Key Takeaways

  • Sets are unordered, mutable collections of unique, immutable items: my_set = {1, 2, 3}
  • Create empty sets with set() (not {}, which creates a dict)
  • Remove duplicates instantly: set([1, 2, 2, 3]) = {1, 2, 3}
  • Use .add() and .remove() to modify sets; .update() adds multiple items
  • Set operations: union (|), intersection (&), difference (-), symmetric difference (^)
  • Membership testing in sets is O(1), much faster than lists for large collections

What Are the Core Properties of Python Sets?

A set is a collection that is unordered, mutable, and does not allow duplicate items—the three pillars that make sets powerful. When you add an item to a set that already exists, the set ignores it rather than creating a duplicate. Items have no index, so order is never guaranteed; items may appear in different orders across different Python runs. Sets are mutable, meaning you can add or remove items after creation, but the items themselves must be immutable types (strings, numbers, tuples—not lists or other sets).

# Create sets with unique items
fruits = {"apple", "banana", "cherry"}
print(fruits) # Order may vary: {'apple', 'cherry', 'banana'}

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

# Convert a list to a set to remove duplicates
numbers_list = [1, 2, 2, 3, 4, 3]
numbers_set = set(numbers_list)
print(numbers_set) # Output: {1, 2, 3, 4}

# Check membership (very fast, O(1))
if "apple" in fruits:
print("Found apple!")

How Do You Create and Modify Sets?

Creating sets is straightforward: use curly braces {1, 2, 3} for non-empty sets or the set() constructor for empty sets. Never use {} for an empty set; that creates an empty dictionary. Once created, modify sets with .add() to insert single items, .update() to add multiple items from an iterable, and .remove() or .discard() to delete items.

# Creating sets
fruits = {"apple", "banana", "cherry"}
print(f"Original: {fruits}") # Output: {'apple', 'banana', 'cherry'}

# Create from a list (removes duplicates)
numbers_list = [1, 2, 2, 3, 4, 3]
numbers_set = set(numbers_list)
print(f"From list: {numbers_set}") # Output: {1, 2, 3, 4}

# Create an empty set (NOT {})
empty_set = set()
print(f"Type of set(): {type(empty_set)}") # Output: <class 'set'>

empty_dict = {}
print(f"Type of {{}}: {type(empty_dict)}") # Output: <class 'dict'>

Adding and removing items:

skills = {"Python", "Git"}
print(f"Original: {skills}")

# Add a single item
skills.add("SQL")
print(f"After .add('SQL'): {skills}")

# Add multiple items from a list
skills.update(["HTML", "CSS"])
print(f"After .update(['HTML', 'CSS']): {skills}")

# Remove an item (raises KeyError if not found)
skills.remove("Git")
print(f"After .remove('Git'): {skills}")

# Discard an item (no error if not found)
skills.discard("JavaScript") # 'JavaScript' not in set—no error
print(f"After .discard('JavaScript'): {skills}")

# Remove and return an arbitrary item
removed = skills.pop()
print(f"Popped: {removed}")

# Clear all items
skills.clear()
print(f"After .clear(): {skills}") # Output: set()

What Are Set Operations and Why Use Them?

Set operations—union, intersection, difference, and symmetric difference—model mathematical set theory and enable efficient comparison between collections. These operations create new sets rather than modifying the originals, and they're highly optimized in Python. They're invaluable for finding common elements, combining lists without duplicates, and filtering data.

# Define two sets of developer skills
dev1_skills = {"Python", "JavaScript", "HTML", "CSS"}
dev2_skills = {"Python", "SQL", "HTML", "PowerBI"}

# Union: All unique skills from both developers
all_skills = dev1_skills.union(dev2_skills) # Also: dev1_skills | dev2_skills
print(f"Union: {all_skills}")
# Output: {'Python', 'JavaScript', 'HTML', 'CSS', 'SQL', 'PowerBI'}

# Intersection: Skills both developers share
common_skills = dev1_skills.intersection(dev2_skills) # Also: dev1_skills & dev2_skills
print(f"Intersection: {common_skills}")
# Output: {'Python', 'HTML'}

# Difference: Skills dev1 has that dev2 doesn't
unique_to_dev1 = dev1_skills.difference(dev2_skills) # Also: dev1_skills - dev2_skills
print(f"Difference: {unique_to_dev1}")
# Output: {'JavaScript', 'CSS'}

# Symmetric Difference: Skills in either set, but not both
unique_overall = dev1_skills.symmetric_difference(dev2_skills) # Also: dev1_skills ^ dev2_skills
print(f"Symmetric Difference: {unique_overall}")
# Output: {'JavaScript', 'CSS', 'SQL', 'PowerBI'}

Operation reference:

OperationMethodOperatorDescription
Union.union(other)|All items from both sets
Intersection.intersection(other)&Only items in both sets
Difference.difference(other)-Items in first set, not in second
Symmetric Difference.symmetric_difference(other)^Items in either set, not both
Subset.issubset(other)<=True if all items are in other
Superset.issuperset(other)>=True if contains all items of other

How Do You Compare and Combine Multiple Sets?

Beyond the basic operations, you can check relationships between sets using .issubset(), .issuperset(), and .isdisjoint(). These return True or False and are useful for validation. You can also chain operations to combine multiple sets in one expression.

# Check relationships between sets
set1 = {1, 2}
set2 = {1, 2, 3, 4}
set3 = {5, 6}

print(set1.issubset(set2)) # Output: True (all items of set1 are in set2)
print(set2.issuperset(set1)) # Output: True (set2 contains all of set1)
print(set1.isdisjoint(set3)) # Output: True (no items in common)

# Chaining operations
a = {1, 2, 3}
b = {2, 3, 4}
c = {3, 4, 5}

result = a | b & c # Union of a with the intersection of b and c
print(result) # Output: {1, 2, 3, 4}

Real-World Example: Analyzing Developer Skills

Here's a complete program that uses set operations to analyze and compare skill sets between developers:

def analyze_developer_skills(dev1_skills, dev2_skills):
"""
Analyzes and prints the relationship between two sets of skills.

Args:
dev1_skills: List or set of Developer 1's skills
dev2_skills: List or set of Developer 2's skills
"""
# Convert to sets to remove duplicates and enable set operations
dev1_set = set(dev1_skills)
dev2_set = set(dev2_skills)

# Perform all set operations
common = dev1_set.intersection(dev2_set)
unique_to_dev1 = dev1_set.difference(dev2_set)
unique_to_dev2 = dev2_set.difference(dev1_set)
all_skills = dev1_set.union(dev2_set)

# Print results
print("--- Skill Analysis ---")
print(f"Common Skills: {common if common else 'None'}")
print(f"Unique to Dev1: {unique_to_dev1 if unique_to_dev1 else 'None'}")
print(f"Unique to Dev2: {unique_to_dev2 if unique_to_dev2 else 'None'}")
print(f"Total Skill Pool: {all_skills}")
print(f"Number of shared skills: {len(common)}")
print(f"Total unique skills: {len(all_skills)}")

# Run the analysis
developer1_skills = ["Python", "JavaScript", "React", "CSS", "Git"]
developer2_skills = ["Java", "Python", "SQL", "Git", "Docker"]

analyze_developer_skills(developer1_skills, developer2_skills)

# Output:
# --- Skill Analysis ---
# Common Skills: {'Git', 'Python'}
# Unique to Dev1: {'JavaScript', 'React', 'CSS'}
# Unique to Dev2: {'Java', 'SQL', 'Docker'}
# Total Skill Pool: {'Python', 'Java', 'JavaScript', 'React', 'CSS', 'Git', 'SQL', 'Docker'}
# Number of shared skills: 2
# Total unique skills: 8

Frequently Asked Questions

Why can't I add a list to a set?

Sets require immutable items because they use a hashing mechanism to detect duplicates. Lists are mutable (you can change them after creation), so they can't be hashed consistently. Only immutable types—integers, floats, strings, tuples, and frozensets—can be set members. If you need to store collections as set items, use tuples instead: my_set = {(1, 2), (3, 4)}.

What's the difference between .remove() and .discard()?

.remove() raises a KeyError if the item is not in the set, while .discard() silently does nothing. Use .remove() when you want to ensure the item exists; use .discard() when you're unsure and don't care if it's missing. Both remove only the first occurrence (sets can't have duplicates anyway).

How do sets improve performance compared to lists?

Membership testing in sets is O(1) on average, meaning it takes the same time regardless of set size. In lists, membership testing is O(n), meaning it checks every item. For a list of 1 million items, 1000000 in my_list might check all million items, while 1000000 in my_set checks in constant time. For large collections, sets are dramatically faster for membership tests and duplicate removal.

Can I convert a set to a list or dictionary?

Yes. Convert a set to a list with list(my_set), which returns a list with the set's items in arbitrary order. Convert to a dictionary with dict.fromkeys(my_set) to create a dict with set items as keys and None as values. You can also iterate directly: for item in my_set: works just like with lists.

What is a frozenset, and when should I use it?

A frozenset is an immutable version of a set—you can't add or remove items after creation. Because it's immutable, a frozenset can be added to another set or used as a dictionary key, unlike regular sets. Use frozensets when you need set operations but want immutability, or when you need to use a set as a key in a dictionary: my_dict = {frozenset({1, 2}): "value"}.

Further Reading