🕯️ Magic Note
Sets are implemented using hash tables, just like dictionaries. This makes membership testing (checking if an item is in a set) extremely fast. Almost instant, even for huge sets. This is one of Python’s hidden superpowers.
Python
# Creating a set with curly braces
fruits = {“apple”, “banana”, “cherry”}
print(fruits) # {‘banana’, ‘apple’, ‘cherry’} (order may vary)
# Empty set (note: {} is empty dictionary, not set)
empty_set = set()
print(type(empty_set)) # <class ‘set’>
# Wrong way to create empty set
not_a_set = {}
print(type(not_a_set)) # <class ‘dict’> (this is a dictionary!)
# Using set() constructor with other sequences
from_list = set([1, 2, 2, 3, 3, 3]) # {1, 2, 3}
from_string = set(“hello”) # {‘h’, ‘e’, ‘l’, ‘o’}
from_tuple = set((1, 2, 2, 3)) # {1, 2, 3}
Python
my_set = {“red”, “green”, “blue”}
# This does NOT work (no indexing)
# print(my_set[0]) # TypeError: ‘set’ object is not subscriptable
# You cannot slice a set
# print(my_set[0:2]) # TypeError: ‘set’ object is not subscriptable
# Order is not guaranteed
print(my_set) # Might be {‘red’, ‘green’, ‘blue’} or {‘blue’, ‘green’, ‘red’} etc.
🕯️ Magic Note
Python sets do not preserve insertion order. However, from Python 3.7 onward, they remember insertion order as an implementation detail, but you should never rely on it. If you need order, use a list. If you need uniqueness and order, use dict.fromkeys(sequence).
Python
# Duplicates disappear
numbers = {1, 2, 2, 3, 3, 3, 4, 4, 4, 4}
print(numbers) # {1, 2, 3, 4}
# Removing duplicates from a list using set
list_with_dupes = [1, 2, 2, 3, 1, 4, 2, 5, 3]
unique_list = list(set(list_with_dupes))
print(unique_list) # [1, 2, 3, 4, 5] (order may vary)
# If you need to preserve order while removing duplicates
ordered_unique = list(dict.fromkeys(list_with_dupes))
print(ordered_unique) # [1, 2, 3, 4, 5] (order preserved)
Python
colors = {“red”, “green”}
# .add() adds one item
colors.add(“blue”)
print(colors) # {‘red’, ‘green’, ‘blue’}
# Adding an existing item does nothing (no error, no change)
colors.add(“red”)
print(colors) # {‘red’, ‘green’, ‘blue’} (unchanged)
# .update() adds multiple items (iterable)
colors.update({“yellow”, “purple”})
print(colors) # {‘yellow’, ‘purple’, ‘red’, ‘green’, ‘blue’}
# .update() works with any iterable (list, tuple, string)
colors.update([“orange”, “pink”])
print(colors) # {‘orange’, ‘pink’, …}
| Method | What It Does | If Item Missing |
|---|---|---|
| .remove(x) | Removes x from the set | Raises KeyError |
| .discard(x) | Removes x from the set | Does nothing (no error) |
| .pop() | Removes and returns an arbitrary item | Raises KeyError if set is empty |
| .clear() | Removes all items from the set | Always works |
Python
colors = {“red”, “green”, “blue”, “yellow”}
# .remove() raises error if item not found
colors.remove(“green”)
print(colors) # {‘red’, ‘blue’, ‘yellow’}
# colors.remove(“purple”) # KeyError: ‘purple’
# .discard() does nothing if item not found
colors.discard(“purple”) # No error
print(colors) # {‘red’, ‘blue’, ‘yellow’}
# .pop() removes an arbitrary item (you don’t know which)
removed = colors.pop()
print(removed) # Could be ‘red’, ‘blue’, or ‘yellow’
# .clear() removes everything
colors.clear()
print(colors) # set()
Python
fruits = {“apple”, “banana”, “cherry”}
print(len(fruits)) # 3
print(“banana” in fruits) # True
print(“grape” in fruits) # False
print(“banana” not in fruits) # False
print(“grape” not in fruits) # True
# Empty set is falsy
empty = set()
if empty:
print(“This won’t print”)
else:
print(“Empty set is falsy”)
Python
colors = {“red”, “green”, “blue”}
for color in colors:
print(color)
# Output order may vary each time you run the code
| Operation | Method | Operator | What It Does |
|---|---|---|---|
| Union | .union() | | | All items from both sets (no duplicates) |
| Intersection | .intersection() | & | Items that appear in both sets |
| Difference | .difference() | – | Items in first set but not in second |
| Symmetric Difference | .symmetric_difference() | ^ | Items in either set but not both |
Python
A = {1, 2, 3, 4}
B = {3, 4, 5, 6}
# Union (all items from both sets)
print(A | B) # {1, 2, 3, 4, 5, 6}
print(A.union(B)) # {1, 2, 3, 4, 5, 6}
# Intersection (items in both sets)
print(A & B) # {3, 4}
print(A.intersection(B)) # {3, 4}
# Difference (items in A but not in B)
print(A – B) # {1, 2}
print(A.difference(B)) # {1, 2}
# Symmetric Difference (items in either but not both)
print(A ^ B) # {1, 2, 5, 6}
print(A.symmetric_difference(B)) # {1, 2, 5, 6}
🕯️ Magic Note
The vertical bar | for union and ampersand & for intersection are intuitive once you think of them as “or” and “and” for sets. These operators are rarely used in other Python contexts, but they are perfect for sets.
Python
A = {1, 2, 3, 4}
B = {1, 2}
C = {5, 6}
# .issubset() checks if all items are in the other set
print(B.issubset(A)) # True (B is fully inside A)
print(A.issubset(B)) # False (A has items not in B)
# .issuperset() checks if set contains all items of the other
print(A.issuperset(B)) # True (A contains B)
print(B.issuperset(A)) # False
# .isdisjoint() checks if sets have no common items
print(A.isdisjoint(C)) # True (A and C share nothing)
print(A.isdisjoint(B)) # False (they share 1 and 2)
Python
# Set of squares
squares = {x**2 for x in range(1, 6)}
print(squares) # {1, 4, 9, 16, 25}
# Set of even numbers with filter
evens = {x for x in range(10) if x % 2 == 0}
print(evens) # {0, 2, 4, 6, 8}
# Set of unique characters from a string
unique_chars = {char for char in “mississippi”}
print(unique_chars) # {‘m’, ‘i’, ‘s’, ‘p’}
Python
# Creating a frozen set
normal_set = {1, 2, 3}
frozen = frozenset([1, 2, 3, 3, 2])
print(frozen) # frozenset({1, 2, 3})
# Frozen sets are immutable (cannot add or remove)
# frozen.add(4) # AttributeError: ‘frozenset’ object has no attribute ‘add’
# Frozen sets can be dictionary keys (regular sets cannot)
my_dict = {
frozenset({1, 2}): “pair”,
frozenset({1, 2, 3}): “triple”
}
print(my_dict[frozenset({1, 2})]) # pair
# But they still support set operations
a = frozenset([1, 2, 3])
b = frozenset([2, 3, 4])
print(a | b) # frozenset({1, 2, 3, 4})
Python
# Valid set items (immutable)
valid = {1, 2, 3} # numbers
valid = {“a”, “b”, “c”} # strings
valid = {(1, 2), (3, 4)} # tuples
# Invalid set items (mutable) – cause TypeError
# invalid = {[1, 2], [3, 4]} # TypeError: unhashable type: ‘list’
# invalid = {{1, 2}, {3, 4}} # TypeError: unhashable type: ‘set’
# invalid = {{“a”: 1}} # TypeError: unhashable type: ‘dict’
- Creating an empty set with {} (creates a dictionary, not a set)
- Expecting sets to maintain order (they do not)
- Trying to access items by index: my_set[0]
- Using mutable items (lists, dictionaries) in a set
- Confusing .add() (one item) with .update() (multiple items)
- Using .remove() when unsure of membership (use .discard() instead)
- How do you create an empty set?
- What is the output of set([1, 2, 2, 3, 1])?
- What is the difference between .remove() and .discard()?
- Write code to find the common items between two sets A and B.
- Why can’t you use a list as a set item?
- What is a frozen set and when would you use it?
⚡ Whisper
A set is a collector of unique whispers. It does not care about the order they arrived. It does not keep count. It only remembers that each whisper was heard. Once. Not twice. Not three times. Just once. When you need to know if a word has been spoken, a set answers faster than any list. When you need to find what two groups share, a set shows you with a single symbol. When you need to remove every echo and duplicate, a set silences them all. The set is not confused by repetition. The set remembers only the truth. One truth at a time.