Sets are used to store unordered collections
of unique values
A set is an unordered collection of distinct items. Unordered means that items aren’t stored in any particular order. Something is either in the set or it’s not, but there’s no notion of it being the first, second, or last item. Distinct means that any item appears in a set at most once; there are no duplicates.
Python has a type called set that allows you to store mutable collections of unordered, distinct items. (Remember that a mutable object is one that you can modify.) Let’s create a set containing the vowels:
| | >>> vowels = {'a', 'e', 'i', 'o', 'u'} |
| | >>> vowels |
| | {'a', 'u', 'o', 'i', 'e'} |
Searching through sets and dictionaries is very fast
It resembles a list, except that sets use braces ({}) instead of brackets ([]). Notice that, when displayed in the shell, the set is shown in an unordered manner. Python does some mathematical tricks behind the scenes to make accessing the items very fast, and one of the side effects of this is that the items aren’t in any particular order.
Let’s show that each item is distinct; duplicates are ignored:
| | >>> vowels = {'a', 'e', 'a', 'a', 'i', 'o', 'u', 'u'} |
| | >>> vowels |
| | {'u', 'o', 'i', 'e', 'a'} |
Even though there were three ’a’s and two ’u’s when you created the set, only one of each was kept. Python considers the two sets to be equal:
| | >>> {'a', 'e', 'i', 'o', 'u'} == {'a', 'e', 'a', 'a', 'i', 'o', 'u', 'u'} |
| | True |
The reason they are equal is that they contain the same items. Again, order doesn’t matter, and only one of each element is kept.
Variable vowels refers to an object of type set:
| | >>> type(vowels) |
| | <class 'set'> |
| | >>> type({1, 2, 3}) |
| | <class 'set'> |
In , you’ll learn about a type that also uses the notation {}, which prevents you from using that notation to represent an empty set. Instead, to create an empty set, you need to call the function set with no arguments:
| | >>> set() |
| | set() |
| | >>> type(set()) |
| | <class 'set'> |
The function set is a constructor. It expects either no arguments (to create an empty set) or a single argument that is a collection of values. For example, let’s create a set from a list:
| | >>> set([2, 3, 2, 5]) |
| | {2, 3, 5} |
Because duplicates aren’t allowed, only one of the 2s appears in the set:

The function set expects at most one argument. You can’t pass several values as separate arguments:
| | >>> set(2, 3, 5) |
| | Traceback (most recent call last): |
| | File "<python-input-0>", line 1, in <module> |
| | set(2, 3, 5) |
| | ~~~^^^^^^^^^ |
| | TypeError: set expected at most 1 argument, got 3 |
In addition to lists, there are a couple of other types that can be used as arguments to the function set. One is a set:
| | >>> vowels = {'a', 'e', 'a', 'a', 'i', 'o', 'u', 'u'} |
| | >>> vowels |
| | {'i', 'a', 'u', 'e', 'o'} |
| | >>> set(vowels) |
| | {'i', 'a', 'u', 'e', 'o'} |
| | >>> set({5, 3, 1}) |
| | {1, 3, 5} |
Another such type is range from . In the following code, a set is created with the values 0 to 4 inclusive:
| | >>> set(range(5)) |
| | {0, 1, 2, 3, 4} |
In , you will learn about the tuple type, another type of sequence, that can also be used as an argument to the function set.
In mathematics, set operations include union, intersection, add, and remove. In Python, these are implemented as methods:
| Method | Description |
|---|---|
| S.add | Adds item v to a set S—this has no effect if v is already in S |
| S.clear | Removes all items from set S |
| S.difference | Returns a set with items that occur in set S but not in set other |
| S.intersection | Returns a set with items that occur both in sets S and other |
| S.issubset | Returns True if and only if all of set S’s items are also in set other |
| S.issuperset | Returns True if and only if set S contains all of set other’s items |
| S.remove | Removes item v from set S |
| S.symmetric_difference | Returns a set with items that are in exactly one of sets S and other—any items that are in both sets are not included in the result |
| S.union | Returns a set with items that are either in set S or other (or in both) |
Sets are mutable. The methods add, remove, and clear all modify a set. The letter y is sometimes considered to be a vowel; let’s add it to the set of vowels:
| | >>> vowels = {'a', 'e', 'i', 'o', 'u'} |
| | >>> vowels |
| | {'o', 'u', 'a', 'e', 'i'} |
| | >>> vowels.add('y') |
| | >>> vowels |
| | {'u', 'y', 'e', 'a', 'o', 'i'} |
Other methods, such as intersection and union, return new sets based on their arguments.
In the following code, you’ll see all of these methods in action:
| | >>> ten = set(range(10)) |
| | >>> lows = {0, 1, 2, 3, 4} |
| | >>> odds = {1, 3, 5, 7, 9} |
| | >>> lows.add(9) |
| | >>> lows |
| | {0, 1, 2, 3, 4, 9} |
| | >>> lows.difference(odds) |
| | {0, 2, 4} |
| | >>> lows.intersection(odds) |
| | {1, 3, 9} |
| | >>> lows.issubset(ten) |
| | True |
| | >>> lows.issuperset(odds) |
| | False |
| | >>> lows.remove(0) |
| | >>> lows |
| | {1, 2, 3, 4, 9} |
| | >>> lows.symmetric_difference(odds) |
| | {2, 4, 5, 7} |
| | >>> lows.union(odds) |
| | {1, 2, 3, 4, 5, 7, 9} |
| | >>> lows.clear() |
| | >>> lows |
| | set() |
Many of the tasks performed by methods can also be accomplished using operators. If acids and bases are two sets, for example, then acids | bases creates a new set containing their union (that is, all the elements from both acids and bases). In contrast, acids <= bases tests whether acids is a subset of bases—that is, that all the values in acids are also in bases. Some of the operators that sets support are listed in the following table.
| Method Call | Operator |
|---|---|
| some_set.difference | some_set - other_set |
| some_set.intersection | some_set & other_set |
| some_set.issubset | some_set <= other_set |
| some_set.issuperset | some_set >= other_set |
| some_set.union | some_set | other_set |
| some_set.symmetric_difference | some_set ^ other_set |
The following code shows the set operations in action.
| | >>> lows = set([0, 1, 2, 3, 4]) |
| | >>> odds = set([1, 3, 5, 7, 9]) |
| | >>> lows - odds # Equivalent to lows.difference(odds) |
| | {0, 2, 4} |
| | >>> lows & odds # Equivalent to lows.intersection(odds) |
| | {1, 3} |
| | >>> lows <= odds # Equivalent to lows.issubset(odds) |
| | False |
| | >>> lows >= odds # Equivalent to lows.issuperset(odds) |
| | False |
| | >>> lows | odds # Equivalent to lows.union(odds) |
| | {0, 1, 2, 3, 4, 5, 7, 9} |
| | >>> lows ^ odds # Equivalent to lows.symmetric_difference(odds) |
| | {0, 2, 4, 5, 7, 9} |
Suppose you have a file used to record observations of birds in the Canadian Arctic, and you want to know which species have been observed. The observations file, observations.txt, has one species per line:
| | canada goose |
| | canada goose |
| | long-tailed jaeger |
| | canada goose |
| | snow goose |
| | canada goose |
| | long-tailed jaeger |
| | canada goose |
| | northern fulmar |
The following program reads each line of the file, strips off the leading and trailing whitespace, and adds the species on that line to the set. Notice the type annotation specifying that the function returns a set of strings.
| | from typing import Set, TextIO |
| | from io import StringIO |
| | |
| | def observe_birds(observations_file: TextIO) -> Set[str]: |
| | """Return a set of the bird species listed in observations_file, which has |
| | one bird species per line. |
| | |
| | >>> infile = StringIO('bird 1\\nbird 2\\nbird 1\\n') |
| | >>> birds = observe_birds(infile) |
| | >>> birds == {'bird 1', 'bird 2'} |
| | True |
| | """ |
| | birds_observed = set() |
| | for line in observations_file: |
| | bird = line.strip() |
| | birds_observed.add(bird) |
| | |
| | return birds_observed |
| | |
| | if __name__ == '__main__': |
| | with open('observations.txt') as observations_file: |
| | print(observe_birds(observations_file)) |
The resulting set contains four species. Since sets don’t include duplicates, calling the method add with a species already in the set had no effect.
You can loop over the values in a set. In the following code, a for loop is used to print each species:
| | >>> for species in birds_observed: |
| | ... print(species) |
| | ... |
| | long-tailed jaeger |
| | canada goose |
| | northern fulmar |
| | snow goose |
Looping over a set works precisely like a loop over a list, except that the order in which items are encountered is arbitrary; there is no guarantee that they will come out in the order in which they were added, in alphabetical order, in order by length, or any other order.
Sets employ a mathematical technique called hashing, which relies on immutable set values. Mutable values, such as lists, cannot be added to sets because mutable values in general are unhashable.
| | >>> S = set() |
| | >>> L = [1, 2, 3] |
| | >>> S.add(L) |
| | Traceback (most recent call last): |
| | File "<python-input-2>", line 1, in <module> |
| | S.add(L) |
| | ~~~~~^^^ |
| | TypeError: cannot use 'list' as a set element (unhashable type: 'list') |
This restriction means that you can’t store a set of sets. Sets themselves can’t be immutable, since you need to add and remove values, so a set can’t contain another one. To solve this problem, Python has another data type called a frozen set. As the name implies, frozen sets are sets that cannot be mutated. An empty frozen set is created using the frozenset constructor; to create a frozen set that contains some values, use frozenset(values), where values is a list, tuple, set, or other collection.
In the next section, you will learn about tuples, which can also be used as items in sets.