Skip to content

Sets

~15 min ยท prerequisite: Lists

What you will be able to do

  • Create a set that holds each value at most once, and add or remove values.
  • Test membership with in, and combine sets with union, intersection, and difference.
  • Explain why a set has no positions, and sort a set when you need an ordered result.

The idea

A set is a collection with two rules. It stores each value at most once, and it does not keep the values in any position you can read. A set is written with curly braces, such as {1, 2, 3}. If you write {1, 2, 2, 3}, the second 2 is ignored, so the set holds three values.

The empty set needs a special form. The expression {} creates an empty dictionary, so write set() to create an empty set. Sets, like dictionaries, can hold only hashable values. Numbers, strings, and tuples of those are allowed. Lists and dictionaries are not.

Sets are quick at one task: asking whether a value is present. The check x in my_set does not need to look through every item one by one, which makes it a good choice when you check membership many times. Since sets have no positions, my_set[0] is an error. To read the values in order, use sorted(my_set), which returns a sorted list.

The methods add(x) and discard(x) change a set. The method discard(x) does nothing if x is missing. The method remove(x) raises an error when x is missing, so use it only when a missing value is a bug.

Sets also support operations you may know from mathematics. The union, written a | b, has every value in either set. The intersection, a & b, has values in both. The difference, a - b, has values in a that are not in b. The operator <= tests whether every value of the left set is in the right set, which is called a subset.

Worked example

visited = {"home", "about", "home", "blog"}
print(len(visited))
print("blog" in visited)

visited.add("contact")
visited.discard("missing")
print(sorted(visited))

mine = {1, 2, 3, 4}
yours = {3, 4, 5}
print(sorted(mine | yours))
print(sorted(mine & yours))
print(sorted(mine - yours))

Line by line, this is what Python does:

  1. The duplicate "home" is ignored, so the set holds three values. len returns 3.
  2. "blog" in visited checks membership and gives True.
  3. add("contact") puts a new value in the set. discard("missing") tries to remove a value that is not there, and nothing happens.
  4. sorted(visited) returns a sorted list, so the printed order is predictable.
  5. The union mine | yours holds 1, 2, 3, 4, 5. The intersection mine & yours holds 3 and 4. The difference mine - yours holds 1 and 2.

The output is:

3
True
['about', 'blog', 'contact', 'home']
[1, 2, 3, 4, 5]
[3, 4]
[1, 2]

Try it

The first block checks duplicates and add. The second block checks the set operations. The third block shows an empty set and the difference between {} and set().

tags = {"python", "code", "python"}
assert len(tags) == 2
assert "code" in tags
tags.add("course")
assert len(tags) == 3
tags.discard("missing")
assert len(tags) == 3
a = {1, 2, 3, 4}
b = {3, 4, 5}
assert a | b == {1, 2, 3, 4, 5}
assert a & b == {3, 4}
assert a - b == {1, 2}
assert {3, 4} <= a
assert not ({1, 9} <= a)
empty_set = set()
empty_dict = {}
assert type(empty_set) is set
assert type(empty_dict) is dict
assert len(empty_set) == 0
assert sorted({5, 1, 3}) == [1, 3, 5]

Common mistakes

  1. Using {} for an empty set. The code seen = {} followed by seen.add(4) gives:

    AttributeError: 'dict' object has no attribute 'add'
    

    The expression {} is an empty dictionary. Write seen = set() instead.

  2. Indexing a set. The code values = {10, 20} followed by print(values[0]) gives:

    TypeError: 'set' object is not subscriptable
    

    A set has no positions. Use in to test for a value, or sorted(values) to get an ordered list.

  3. Putting a list inside a set. The code groups = {[1, 2], [3]} gives:

    TypeError: unhashable type: 'list'
    

    Use a tuple, such as {(1, 2), (3,)}, because tuples can be stored in a set.

  4. Removing a value that is not there. The code s = {1, 2} followed by s.remove(5) gives:

    KeyError: 5
    

    Use discard(5) when a missing value is acceptable, or check with if 5 in s: first.

  5. Relying on the order of a set. Printing {3, 1, 2} may show the values in a different order. Compare sets with ==, or sort them before you print or compare them as lists.

Practice

  1. Create colors = {"red", "blue"}. Add "green" and assert that len(colors) is 3.
  2. Create nums = {1, 2, 2, 3}. Assert that len(nums) is 3, and that 2 in nums is True.
  3. Create a = {1, 2, 3} and b = {2, 3, 4}. Assert that a & b equals {2, 3} and that a | b equals {1, 2, 3, 4}.
  4. Create words = ["cat", "dog", "cat", "emu"]. Build a set unique from this list with set(words). Assert that len(unique) is 3, and that sorted(unique) equals ["cat", "dog", "emu"].
  5. Stretch: create required = {"name", "email", "age"} and given = {"name", "age", "city"}. Use the difference operator to find the required fields that are missing. Assert that the result equals {"email"}.
Solution
  1. python colors = {"red", "blue"} colors.add("green") assert len(colors) == 3

  2. python nums = {1, 2, 2, 3} assert len(nums) == 3 assert (2 in nums) is True

  3. python a = {1, 2, 3} b = {2, 3, 4} assert a & b == {2, 3} assert a | b == {1, 2, 3, 4}

  4. python words = ["cat", "dog", "cat", "emu"] unique = set(words) assert len(unique) == 3 assert sorted(unique) == ["cat", "dog", "emu"]

  5. python required = {"name", "email", "age"} given = {"name", "age", "city"} missing = required - given assert missing == {"email"}

Recap

  • A set holds each hashable value at most once, and has no positions to index.
  • Use set() for an empty set, because {} is a dictionary.
  • Use |, &, and - to combine and compare sets, and sorted(...) when you need an ordered list.

Sources