Sets
By the Syncfusion DevTutorials team · Last updated
Unique unordered values: creating sets, add and discard, union, intersection and difference, the whole method list, and frozen sets.
Think about a guest list for a school event.
Each name appears once. Writing a name twice changes nothing — the person is either invited or not. And nobody cares what order the names are in; the only question anyone asks is "is this person on the list?"
That is a set: no duplicates, no order, and a very fast way to ask whether something is in it.
Here is that idea in action — five names go in, and only the distinct ones stay:
Duplicates vanish on their own
invited = {"Asha", "Ravi", "Asha", "Meera", "Ravi"}
print(len(invited))
print(sorted(invited))
print("Asha" in invited)
Five names went in and three came out. The set kept one of each.
A set holds unique values with no order, and answers "is this in here?" very quickly.
What You Will Learn
In this lesson, you will learn:
- What makes a set different from a list, and when that difference matters
- How to create one, including the empty-set trap
- How to add and remove items safely
- The four set operations, which answer real questions in one line
- The whole method list, and which methods change the set
- What a frozen set is, and when you need one
By the end of this lesson, you will be able to create sets, add and remove items safely, use the four set operations to compare groups, pick the right method for each job, and decide when a frozen set is needed.
Python Sets
Introduction to Sets
A set is a collection of unique values with no order.
It is the right tool for three jobs: removing duplicates, asking whether something is present, and comparing two groups.
Creating Sets
There are several ways to make a set, but one of them also solves the common problem of removing duplicates from another collection:
Three ways to make a set
a = {78, 85, 60}
b = set([78, 85, 60])
c = set("Asha")
print(sorted(a))
print(sorted(b))
print(sorted(c))
Set Characteristics
Most of a set's characteristics follow from one idea — a set forgets both duplicates and positions:
| Characteristic | What it means |
|---|---|
| Unique | duplicates are dropped, silently |
| Unordered | there is no first item, and no indexing |
| Changeable | you can add and remove items |
| Hashable items only | numbers, text and tuples — not lists |
| Very fast lookup | in does not search item by item |
Unique Elements in Sets
Dropping duplicates is half of what a set is. Here it is working on a list:
Duplicates are dropped
marks = [78, 85, 78, 60, 85, 78]
unique = set(marks)
print("original:", marks)
print("unique: ", sorted(unique))
print(len(marks), "->", len(unique))
Removing duplicates from a list is the single most common use:
Unique, and back to a list
marks = [78, 85, 78, 60]
print(sorted(set(marks)))
print(list(set(marks)) != [])
Note the sorted(). Turning a set back into a list gives no promise about order, so sort it if the order matters.
Unordered Nature of Sets
A set has no positions, so there is no indexing and no slicing.
A set cannot be indexed
marks = {78, 85, 60}
print(marks[0])
Printing a set shows its items in whatever order it happens to store them. That order is not something to rely on — for text it can differ between runs of the program.
The order you see is not promised
subjects = {"Maths", "Science", "English", "History"}
print(subjects)
print(sorted(subjects))
The second line is the one to use when the order matters to a reader.
Mutable Sets
Although a set has no positions, it can still grow and shrink as items are added and removed:
Sets can be changed
marks = {78, 85}
marks.add(60)
marks.discard(78)
print(sorted(marks))
Data Types Supported in Sets
Everything in a set must be hashable, which in practice means unchangeable.
What may go in a set
mixed = {78, 78.5, "Asha", True, None, (1, 2)}
print(len(mixed))
print(sorted(str(item) for item in mixed))
A list cannot go in a set
{[78, 85]}
A list could change after it went in, which would leave the set unable to find it again. Use a tuple instead.
One oddity worth seeing once, because it also applies to dictionary keys:
True and 1 are the same value
print({1, True, 1.0})
print({0, False})
print(len({1, True}))
True equals 1, so the set keeps only one of them.
Creating Sets
The empty set is the one case that does not work the way you would guess.
Using Curly Braces
Curly braces
marks = {78, 85, 60}
print(sorted(marks), type(marks).__name__)
Using the set() Function
set() from other collections
print(sorted(set([78, 85, 78])))
print(sorted(set((78, 85))))
print(sorted(set("hello")))
print(sorted(set({"maths": 78, "science": 85})))
On a dictionary, set() takes the keys.
Empty Sets
This is the trap.
Empty braces are a dictionary, not a set
not_a_set = {}
real_set = set()
print(type(not_a_set).__name__)
print(type(real_set).__name__)
{} was dictionary syntax long before sets existed, so an empty set has to be written set().
Where that bites
collection = {}
collection.add(78)
AttributeError: 'dict' object has no attribute 'add' — the object was a dictionary all along.
Creating Sets from Lists and Tuples
From a list, and back again
marks = [78, 85, 78, 60]
unique = set(marks)
back_to_list = sorted(unique)
print(unique == {60, 78, 85})
print(back_to_list)
Working with Set Elements
Now that you can make a set, here is how to put items in and take them out.
Adding Elements with add()
add() puts in one item. Adding something already there does nothing, with no complaint.
add
marks = {78, 85}
marks.add(60)
marks.add(78)
print(sorted(marks))
Removing Elements with remove()
remove() deletes an item and raises if it is not there.
remove on a missing item
marks = {78, 85}
marks.remove(78)
print(sorted(marks))
marks.remove(99)
Removing Elements with discard()
discard() does the same and says nothing when the item is missing.
discard never complains
marks = {78, 85}
marks.discard(78)
marks.discard(99)
print(sorted(marks))
Use remove() when the item ought to be there and its absence is a bug. Use discard() when you simply want it gone.
pop() removes an arbitrary item — not the first, because there is no first:
pop takes whichever item it likes
marks = {78}
print(marks.pop())
print(marks)
pop on an empty set
set().pop()
Clearing a Set
clear() empties the whole set at once:
clear
marks = {78, 85}
marks.clear()
print(marks, len(marks))
Updating a Set
update() adds everything from another collection.
update
marks = {78, 85}
marks.update([60, 92])
marks.update({45}, (78,))
marks.update("ab")
print(sorted(str(item) for item in marks))
add() puts in one item; update() puts in each item of whatever you give it — including each character of a string, which surprises people.
update("Asha") adds four separate characters, not the word. A string is a
collection of characters, and update() takes each item of whatever it is
given. Use add("Asha") when you mean the whole word.
Membership Testing
Using the in Operator
This is the question sets were built to answer:
in
invited = {"Asha", "Ravi", "Meera"}
print("Asha" in invited)
print("Ken" in invited)
print("Ken" not in invited)
A list checks items one by one; a set jumps straight to the answer, however large it is.
The same question, two collections
big_list = list(range(50000))
big_set = set(big_list)
print(49999 in big_list)
print(49999 in big_set)
print("Both correct — but the set did far less work")
For a handful of items the difference does not matter. For thousands, checked repeatedly, it is the difference between quick and unusable.
When a program keeps asking "have I seen this before?", build a set once and ask it, rather than searching a list every time. That one change turns a slow loop into a quick one more often than any other single fix.
Set Operations
The four operations are what make sets worth learning. Each answers a question that would otherwise need a loop.
The examples all use these two groups of students:
Two groups to compare
maths = {"Asha", "Ravi", "Meera"}
science = {"Ravi", "Meera", "Ken"}
print("Maths: ", sorted(maths))
print("Science:", sorted(science))
Union
Everyone in either group.
union
maths = {"Asha", "Ravi", "Meera"}
science = {"Ravi", "Meera", "Ken"}
print(sorted(maths.union(science)))
print(sorted(maths | science))
Intersection
Only those in both.
intersection
maths = {"Asha", "Ravi", "Meera"}
science = {"Ravi", "Meera", "Ken"}
print(sorted(maths.intersection(science)))
print(sorted(maths & science))
Difference
In the first group and not the second. Order matters here.
difference
maths = {"Asha", "Ravi", "Meera"}
science = {"Ravi", "Meera", "Ken"}
print(sorted(maths.difference(science)))
print(sorted(science - maths))
Symmetric Difference
In one group or the other, but not both.
symmetric_difference
maths = {"Asha", "Ravi", "Meera"}
science = {"Ravi", "Meera", "Ken"}
print(sorted(maths.symmetric_difference(science)))
print(sorted(maths ^ science))
All four, side by side:
| Operation | Operator | Method | Answers |
|---|---|---|---|
| Union | | | union() | in either |
| Intersection | & | intersection() | in both |
| Difference | - | difference() | in the first only |
| Symmetric difference | ^ | symmetric_difference() | in one but not both |
The methods accept any collection; the operators need both sides to be sets.
An operator needs two sets
marks = {78, 85}
print(sorted(marks.union([60])))
print(marks | [60])
Subset Testing
A subset is a group whose members all appear inside another group:
issubset
everyone = {"Asha", "Ravi", "Meera", "Ken"}
maths = {"Asha", "Ravi"}
print(maths.issubset(everyone))
print(maths <= everyone)
print(everyone.issubset(maths))
Superset Testing
A superset is the opposite relationship: it contains every item from another set:
issuperset
everyone = {"Asha", "Ravi", "Meera", "Ken"}
maths = {"Asha", "Ravi"}
print(everyone.issuperset(maths))
print(everyone >= maths)
A set is both a subset and a superset of itself:
A set contains itself
marks = {78, 85}
print(marks.issubset(marks))
print(marks.issuperset(marks))
print(marks < marks)
< means "a subset, and not equal", which is why the last line is False.
Disjoint Sets
Two sets are disjoint when they share nothing.
isdisjoint
maths = {"Asha", "Ravi"}
music = {"Ken", "Priya"}
science = {"Ravi", "Ken"}
print(maths.isdisjoint(music))
print(maths.isdisjoint(science))
a.isdisjoint(b) says the same as not (a & b), and says it more clearly.
Set Methods
You have now met the methods one at a time. Here is the full list, starting with the ones that change the set:
The methods that change the set
marks = {78}
marks.add(85)
print("add: ", sorted(marks))
marks.update([60, 92])
print("update: ", sorted(marks))
marks.remove(92)
print("remove: ", sorted(marks))
marks.discard(99)
print("discard: ", sorted(marks))
copy_of = marks.copy()
marks.clear()
print("clear: ", marks, "copy still:", sorted(copy_of))
Each operation also has an in-place version that changes the set instead of returning a new one:
The in-place versions
maths = {"Asha", "Ravi"}
science = {"Ravi", "Ken"}
both = maths.copy()
both.update(science)
print("update (union):", sorted(both))
shared = maths.copy()
shared.intersection_update(science)
print("intersection_update:", sorted(shared))
only_maths = maths.copy()
only_maths.difference_update(science)
print("difference_update:", sorted(only_maths))
| Method | What it does | Changes the set? |
|---|---|---|
add(x) | adds one item | yes |
update(xs) | adds each item of a collection | yes |
remove(x) | deletes x, raises if missing | yes |
discard(x) | deletes x, silent if missing | yes |
pop() | removes an arbitrary item | yes |
clear() | empties the set | yes |
copy() | a shallow copy | no |
union(xs) | items in either | no |
intersection(xs) | items in both | no |
difference(xs) | items only in this one | no |
symmetric_difference(xs) | items in one but not both | no |
Frozen Sets
One more kind of set exists, and it solves the problem you just saw — a set cannot go inside another set.
Introduction to Frozen Sets
A frozen set is a set that cannot be changed — the tuple to a set's list.
Creating Frozen Sets with frozenset()
frozenset
subjects = frozenset(["Maths", "Science"])
print(sorted(subjects))
print(len(subjects), "Maths" in subjects)
A frozen set refuses to change
subjects = frozenset(["Maths"])
subjects.add("Science")
Mutable vs Immutable Sets
Everything that only reads the set still works:
Everything that reads still works
a = frozenset([78, 85, 60])
b = frozenset([85, 92])
print(sorted(a | b))
print(sorted(a & b))
print(a.issubset({78, 85, 60, 92}))
print(type(a | b).__name__)
Every operation that returns something works. Only the ones that change it are missing.
Use Cases of Frozen Sets
The main one: a frozen set can go inside another set, or be a dictionary key. An ordinary set cannot.
A set cannot contain a set
{{78, 85}}
A frozen set can
groups = {frozenset(["Asha", "Ravi"]), frozenset(["Meera"])}
print(len(groups))
timetable = {
frozenset(["Monday", "Wednesday"]): "Maths",
frozenset(["Tuesday"]): "Science",
}
print(timetable[frozenset(["Wednesday", "Monday"])])
The last line matters: the key was looked up with the days in a different order and still found, because a set has no order.
The other use is a constant that nobody can change by accident:
A constant collection
SCHOOL_DAYS = frozenset(["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"])
def try_to_add(days):
try:
days.add("Saturday")
return "changed it"
except AttributeError:
return "could not change it"
print(try_to_add(SCHOOL_DAYS))
print(len(SCHOOL_DAYS))
Built-in Functions with Sets
The built-ins that work on a set
marks = {78, 85, 60, 92}
print("len: ", len(marks))
print("max: ", max(marks))
print("min: ", min(marks))
print("sum: ", sum(marks))
print("sorted:", sorted(marks))
print("set: ", sorted(set([78, 78, 85])))
sorted() is the usual way to show a set to a person, because it fixes the order.
zip on sets is a bad idea
subjects = ["Maths", "Science"]
marks = [78, 85]
print(list(zip(subjects, marks)))
print(dict(zip(subjects, marks)))
zip() pairs things up by position, and a set has no positions — so zipping two sets pairs values arbitrarily. Use lists or tuples when order matters.
Common mistakes
Before moving on, here are some of the mistakes beginners make with sets.
Each row is one mistake. The middle column is what you will actually see on screen.
| Mistake | What you see | Why it happens | Fix |
|---|---|---|---|
{} for an empty set | AttributeError: 'dict' object has no attribute 'add' | {} is an empty dictionary | set() |
| Indexing a set | TypeError: 'set' object is not subscriptable | A set has no positions | Convert with sorted() or list() |
| Relying on the printed order | Different order on a different run | A set's order is not promised | sorted() before printing |
remove() on a missing item | KeyError | remove() raises; discard() does not | Use discard(), or check with in |
| A list inside a set | TypeError: unhashable type: 'list' | Items must be unchangeable | Use a tuple |
| A set inside a set | TypeError: unhashable type: 'set' | A set can change, so it cannot be hashed | Use frozenset |
add() where update() was meant | The whole collection added as one item | add adds exactly one thing | update to add each item |
update() with a string | Each character added separately | A string is a collection of characters | add("Asha") for the whole word |
| Expecting duplicates to be reported | They vanish silently | Dropping duplicates is the point | Compare len() before and after |
| An operator with a list on one side | TypeError: unsupported operand type(s) | Operators need two sets | Use the method, which accepts any collection |
Expecting pop() to take the first item | Some other item is removed | There is no first item | Use a list if order matters |
What to Remember
- A set holds unique values with no order, and answers
invery quickly. - Duplicates are dropped silently, which is what makes
set(marks)the usual way to find unique values. {}is an empty dictionary. An empty set isset().- A set has no positions, so no indexing and no slicing.
- Never rely on the printed order — use
sorted()whenever a reader will see it. - Items must be hashable: numbers, text and tuples, never lists or sets.
add()adds one item;update()adds each item of a collection, including each character of a string.remove()raises when the item is missing;discard()does not.|union,&intersection,-difference,^symmetric difference — and the methods accept any collection while the operators need two sets.- A frozen set cannot change, so it can be a dictionary key or go inside another set.
Next: Dictionary