GICT knowledge base
Python Sets: Create, Add, Remove, Compare and Copy Items
Published
A Python set holds distinct, hashable values. Use one for membership checks or comparisons such as which services were added or removed. Sets do not provide positional indexing or a guaranteed iteration order. Use a list when positions, duplicates or sequence order are part of your data.
This guide includes creating sets, adding and removing items, replacing a value, set comparisons, copying and immutable sets. All eight examples run independently with Python 3.12.14.
Create a set and understand hashability
Use set() for an empty set; {} creates an empty dictionary. Hashability is the requirement for members, not simply immutability. For example, a tuple containing a list is unhashable even though the tuple itself cannot be reassigned item by item.
services = {"web", "mail", "web"}
assert services == {"web", "mail"}
assert len(set()) == 0
assert isinstance({}, dict)
assert ("web", 443) in {("web", 443)}
assert len({1, True, 1.0}) == 1
for invalid in [["web"], ("web", [443])]:
try:
{invalid}
except TypeError:
pass
else:
raise AssertionError("An unhashable member was accepted")
print(sorted(services))
Values that compare equal, such as 1, True and 1.0, do not become three distinct members. If those distinctions matter, normalize or label your data before building a set.
Add one value or update from an iterable
add() inserts one hashable value. update() consumes an iterable and inserts its members. Both change the existing set and return None. Passing a string to update() inserts its characters, not the string as a single member.
services = {"web"}
result = services.add("mail")
assert result is None
services.add("web")
services.update(["dns", "mail"])
assert services == {"web", "mail", "dns"}
characters = set()
characters.update("web")
assert characters == {"w", "e", "b"}
print(sorted(services))
Remove, discard, pop and clear
remove(value) raises KeyError when the member is missing; that can help detect a broken assumption. discard(value) ignores a missing member. pop() removes an arbitrary member, not a guaranteed random or first member, and raises KeyError on an empty set. clear() empties the existing set.
services = {"web", "mail", "dns"}
services.remove("mail")
services.discard("missing")
before = services.copy()
removed = services.pop()
assert removed in before
assert services == before - {removed}
services.clear()
assert services == set()
for operation in [lambda: services.remove("missing"), services.pop]:
try:
operation()
except KeyError:
pass
else:
raise AssertionError("Expected a missing-member error")
print(services)
Replace a member without indexing
There is no items[0] = replacement operation for a set. Remove the old value and add the replacement. This example uses remove() intentionally so an absent old value is reported. Adding a value already present can reduce the final number of members; it does not preserve a duplicate.
services = {"web", "legacy-mail"}
services.remove("legacy-mail")
services.add("mail")
assert services == {"web", "mail"}
numbers = {1, 2}
numbers.remove(1)
numbers.add(2)
assert numbers == {2}
print(sorted(services))
Compare two sets without changing them
Union | combines members; intersection & finds shared members; difference - finds members present only on the left; symmetric difference ^ finds members present on only one side. These expressions produce new sets. The matching method forms are union(), intersection(), difference() and symmetric_difference().
previous = {"web", "mail"}
current = {"web", "dns"}
assert previous | current == {"web", "mail", "dns"}
assert previous & current == {"web"}
assert current - previous == {"dns"}
assert previous - current == {"mail"}
assert previous ^ current == {"mail", "dns"}
assert current.difference(previous) == {"dns"}
assert previous == {"web", "mail"}
assert current == {"web", "dns"}
print("Added:", sorted(current - previous))
In-place forms such as difference_update() change the original set. Binary operator forms require set operands; named methods can accept other iterables. Check whether you need a new value or a mutation before choosing a method.
Check subsets, supersets and disjointness
A subset has no member outside the other set. A proper subset must also differ from it. Disjoint sets have no shared members. The empty set is a subset of every set.
required = {"web"}
available = {"web", "dns"}
assert required <= available
assert required < available
assert available >= required
assert required.issubset(available)
assert available.issuperset(required)
assert required.isdisjoint({"mail"})
assert set().issubset(available)
assert not available < available
remaining = available.copy()
remaining.difference_update(required)
assert remaining == {"dns"}
assert available == {"web", "dns"}
print(sorted(remaining))
Copying, aliases and del are different
Assignment creates another reference to the same set. copy() makes a shallow new set. del name removes that name’s binding; it does not necessarily destroy the object because other references may remain. Do not change a set’s size while directly iterating over it; collect changes or traverse a copy.
original = {"web", "mail"}
alias = original
separate = original.copy()
alias.add("dns")
assert original == {"web", "mail", "dns"}
assert separate == {"web", "mail"}
del original
assert alias == {"web", "mail", "dns"}
for item in alias.copy():
if item != "web":
alias.remove(item)
assert alias == {"web"}
print(sorted(alias))
Choose an ordered result or a frozenset when needed
Converting a set to a list does not restore the input order. For hashable values, dict.fromkeys() can deduplicate while preserving first occurrence order. Use sorted() when you want a sorted display and the members are mutually orderable. A frozenset is immutable and hashable, so it can itself be a set member or dictionary key.
names = ["mail", "web", "mail", "dns"]
ordered_unique = list(dict.fromkeys(names))
assert ordered_unique == ["mail", "web", "dns"]
assert sorted(set(names)) == ["dns", "mail", "web"]
short_names = {name for name in names if len(name) == 3}
assert short_names == {"web", "dns"}
bundle = frozenset({"web", "dns"})
assert bundle in {frozenset({"dns", "web"})}
labels = {bundle: "basic"}
assert labels[frozenset({"web", "dns"})] == "basic"
print(ordered_unique)
Set membership commonly avoids a full linear scan, but performance depends on hashing, equality checks and the data. Building a set has its own cost. Do not claim an operation is always faster without measuring the actual workload.
Verification: All eight blocks ran independently, including unhashable members, missing removals, empty pop, equal numeric members and alias behavior. The tests compare set contents rather than assuming a display order. Reference: Python set and frozenset methods. For other collection choices, see Python collections.
Related guides
How to Deploy a FastAPI App with Gunicorn and Nginx
Deploy FastAPI behind Nginx with Gunicorn and systemd. Separate application packages, check HTTPS and verify worker replacement and application health.
PythonLearn How to Modify Python Dictionary Values Easily
Learn how to modify Python dictionary values effectively with various methods, including direct assignment, update(), and comprehensions, along with common mistakes to...
PythonPython Set Copy Tutorial: Avoid Common Mistakes and Data Errors
Learn how to copy sets in Python safely and efficiently while avoiding common mistakes. Explore methods like copy(), set(), and deepcopy().
PythonDifferent Methods to Loop Through Sets in Python (Beginner-Friendly Guide)
Learn diverse methods to loop through sets in Python, including practical examples and use cases, ideal for beginners looking to master set operations.
