set.remove()
set.remove(elem) None · Updated March 15, 2026 · Set Methods The .remove() method removes an element from a set if it is present. If the element is not found, it raises a KeyError. This behavior differs from .discard(), which silently does nothing when the element does not exist. Use .remove() when you want to ensure the element was actually in the set.
Syntax
set.remove(elem)
Parameters
| Parameter | Type | Description |
|---|---|---|
elem | any | The element to remove from the set. Must be hashable. |
Raises
KeyError— if the element is not found in the set
Examples
Basic usage
Remove an element that exists:
fruits = {"apple", "banana", "cherry"}
fruits.remove("banana")
print(fruits)
# output: {'apple', 'cherry'}
Removing raises KeyError if not found
Unlike .discard(), .remove() raises an error if the element does not exist:
numbers = {1, 2, 3}
try:
numbers.remove(99)
except KeyError:
print("Element not found!")
# output: Element not found!
This makes .remove() useful when you need to confirm the element was actually present.
Checking before removing
Check if the element exists first if you are unsure:
colors = {"red", "green", "blue"}
if "green" in colors:
colors.remove("green")
print("Removed green")
else:
print("green not in set")
# output: Removed green
Or handle the exception:
tags = {"python", "coding", "tutorial"}
try:
tags.remove("guide")
except KeyError:
print("Tag not found")
# output: Tag not found
Comparing remove() vs discard()
The key difference is error behavior:
my_set = {1, 2, 3}
# Using remove() on existing element - works fine
my_set.remove(2)
print(f"After remove(2): {my_set}")
# output: After remove(2): {1, 3}
# Using discard() on existing element - also works
my_set.discard(1)
print(f"After discard(1): {my_set}")
# output: After discard(1): {3}
# Now both are empty - remove() fails, discard() does not
my_set.remove(99) # KeyError!
my_set.discard(99) # Silent success
Removing in a loop
Be careful when removing items while iterating:
numbers = {1, 2, 3, 4, 5}
# Copy first to avoid "set changed size during iteration"
for num in list(numbers):
if num % 2 == 0:
numbers.remove(num)
print(numbers)
# output: {1, 3, 5}
A safer approach is to use a set comprehension:
numbers = {1, 2, 3, 4, 5}
evens_removed = {n for n in numbers if n % 2 != 0}
print(evens_removed)
# output: {1, 3, 5}
Working with custom objects
The objects must be hashable:
# Strings work fine
words = {"hello", "world"}
words.remove("hello")
print(words)
# output: {'world'}
# Tuples work (they are immutable)
coords = {(0, 0), (1, 1), (2, 2)}
coords.remove((1, 1))
print(coords)
# output: {(0, 0), (2, 2)}
# Lists do not work (unhashable)
try:
my_list = {[1, 2], [3, 4]}
except TypeError as e:
print(f"Error: {e}")
# output: Error: unhashable type: 'list'
Use case: task management
Remove completed tasks and confirm they existed:
pending_tasks = {"review PR", "deploy to staging", "write tests"}
def complete_task(task_name):
try:
pending_tasks.remove(task_name)
print(f"Completed: {task_name}")
return True
except KeyError:
print(f"Task not found: {task_name}")
return False
complete_task("deploy to staging")
complete_task("deploy to staging")
# output: Completed: deploy to staging
# output: Task not found: deploy to staging
Common Patterns
Ensuring an element exists before processing
users = {"alice", "bob", "charlie"}
def assign_role(username):
if username in users:
users.remove(username)
print(f"Removed {username} from pending")
else:
print(f"{username} not in pending users")
assign_role("bob")
assign_role("dave")
# output: Removed bob from pending
# output: dave not in pending users
Set difference with confirmation
def remove_items(target, to_remove):
"""Remove all items in to_remove from target."""
removed_count = 0
for item in to_remove:
try:
target.remove(item)
removed_count += 1
except KeyError:
pass
return removed_count
available = {1, 2, 3, 4, 5}
requested = [2, 4, 6]
count = remove_items(available, requested)
print(f"Removed {count} items: {available}")
# output: Removed 2 items: {1, 3, 5}
Performance
The .remove() method has:
- Average case: O(1) time complexity
- Worst case: O(n) when hash collisions occur
This is the same performance as .discard() — the only difference is the error behavior.
See Also
- set::set.discard() — removes an element without error if not found
- set::set.add() — adds an element to a set
- set::set.pop() — removes and returns an arbitrary element