Skip to content
Theory · Dicts and sets · lesson 7 of 11

Sets

Let's go back to the stall. Next to the price board hangs a second one, "in stock": it holds only names, with no prices. Each name is written once, because there is no point in writing pear twice.

That is how a set works: it stores each value once. For example, in a list of tags the same word may repeat, but you need only the distinct tags. The set function builds a set from a list and drops the repeats:

Python
tags = ["python", "web", "python", "api", "web"]
unique_tags = set(tags)
print(len(tags), len(unique_tags))  # 5 3
print(sorted(unique_tags))  # ['api', 'python', 'web']
print("api" in unique_tags)  # True

Let's go through it line by line:

  1. set(tags) drops the repeats: of the five tags, three distinct ones remain.
  2. print(len(tags), len(unique_tags)) prints 5 3.
  3. sorted(unique_tags) returns a list in alphabetical order, which is how a set is printed in a predictable order.
  4. "api" in unique_tags checks whether the element is there and gives True.

No order and no indices

A set literal lists the elements in curly braces: {1, 2, 3}. The elements have no order: print may show them in any order, so for output a set is wrapped in sorted. There are no indices or slices either: unique_tags[0] raises TypeError: 'set' object is not subscriptable.

This is where the "in stock" board stops being an exact picture. Its rows go from top to bottom, but a set has neither a first element nor a last one.

So set(tags) drops the repeats but does not keep the order of the list. A set fits when the order does not matter: for example, to find out how many different tags a list holds.

The add and remove methods

The add method adds an element, and a repeat changes nothing. The remove method deletes an element:

Python
team = {"ann", "bob"}
team.add("eve")
team.add("ann")
team.remove("bob")
print(sorted(team))  # ['ann', 'eve']

Let's go through it line by line:

  1. team.add("eve") adds a new player.
  2. team.add("ann") changes nothing: "ann" is already in the set.
  3. team.remove("bob") deletes "bob", and ['ann', 'eve'] remain.

If the element is missing, remove stops the program with a KeyError, like reading a missing dict key.

Hashable elements

A set is built like the keys of a dict without the values. Two things follow from this:

  • the elements must be hashable, so a list cannot go into a set: {[1, 2]} raises TypeError: unhashable type: 'list';
  • like a dict, a set finds an element by its hash, without a scan, so in is much faster here than in a long list.

The immutable variant of a set is the frozenset type. It is hashable, so it works as a dict key or an element of another set.

Interviewers like to ask this: "How does set differ from frozenset?" The answer: a set can be changed with add and remove. A frozenset does not change after it is created. In return, a frozenset is hashable, and a set is not.

Operations

Two sets can be compared with three operators. Each operation returns a new set, and the original ones do not change:

Python
team_a = {"ann", "bob", "eve"}
team_b = {"bob", "kim"}
print(sorted(team_a | team_b))  # ['ann', 'bob', 'eve', 'kim']
print(sorted(team_a & team_b))  # ['bob']
print(sorted(team_a - team_b))  # ['ann', 'eve']
print(sorted(team_b - team_a))  # ['kim']

Union |

The | operator gives the union: all the elements of both sets, each one once. "bob" plays in both teams, but in team_a | team_b he appears once.

Intersection &

The & operator gives the intersection: the common elements. Here team_a & team_b is the players of both teams, that is, only "bob".

Difference -

The - operator gives the difference: the elements of the first set that are not in the second. team_a - team_b is those who play only in the first team.

The difference depends on the order of the operands: team_b - team_a gives ['kim']. The union and the intersection do not depend on the order.

Common mistake

An empty set is written as {}:

Python
seen = {}
seen.add("ann")  # ✗
TEXT
AttributeError: 'dict' object has no attribute 'add'

Empty curly braces create a dict, and a dict has no add method. An empty set is created only by calling set().

Key points

  • A set stores each value once, and set(a_list) drops the repeats.
  • A set has no order and no indices, so for output it is wrapped in sorted.
  • The elements of a set must be hashable, and frozenset is an immutable, hashable set.
  • |, & and - give the union, the intersection and the difference; only set() creates an empty set.

Subscribe to take the course

The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.

Sign in another way