Innovate | Connect | Transform
ContactService statusUSD $

GICT knowledge base

Python Sets: Create, Add, Remove, Compare and Copy Items

Published

Python

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

Account services are unavailable until the production backend is configured.