Legends ofPythos
Claim your name
Writing Real Programs

Choosing with match

Lesson 2 of 16

Watch the lesson1:11 · with Sigrid
When you need to pick one of many actions based on a single value, an if/elif chain works but gets noisy fast. Python 3.10 added the match statement: it reads like a switch table and is easier to scan.

Literal patterns

command = 'quit'

match command:
    case 'go':
        print('moving')
    case 'look':
        print('describing room')
    case 'quit' | 'exit':   # '|' means "or"
        print('bye')
    case _:
        print('unknown command')
One value, many exact matches.
case lines test the value that follows match. The first line that matches runs, and execution stops there — no fall-through. _ is a wildcard that catches anything not matched earlier.

Capturing values with sequence patterns

# 'go north' -> ['go', 'north']
words = ['drop', 'sword', 'shield']

match words:
    case []:                      # empty list
        print('nothing typed')
    case ['drop', *items]:        # 'drop' plus any number of items
        print('dropping:', items)
    case [verb]:                  # exactly one word, captured as verb
        print(f'verb is {verb}')
    case [verb, direction]:       # exactly two words
        print(f'{verb} toward {direction}')
Splitting a command into words and matching them.
* is a star pattern: it grabs zero or more remaining elements into a list. Patterns run top to bottom, so the specific 'drop' case comes before the general two-word case: otherwise drop sword would be caught as a verb and a direction.

Mapping patterns and guards

event = {'type': 'move', 'x': 3, 'y': -1}

match event:
    case {'type': 'move', 'x': x, 'y': y} if x > 0:   # a shape plus a guard
        print(f'move right to ({x}, {y})')
    case {'type': 'move', 'x': x, 'y': y}:
        print(f'move to ({x}, {y})')
    case {'type': t}:                                  # any other dict with a 'type' key
        print('got type:', t)
    case _:
        print('not an event')
Matching dictionary keys and adding a condition.
if after a pattern is a guard: the case only matches when both the shape and the condition are true. This keeps your patterns declarative without nesting ifs inside each branch.
A mapping pattern matches any dictionary that has at least those keys: extra keys are fine. That is why the general {'type': t} case has to come last.

Your turn

0 of 3 solved

Exercise 1

+35 XP
Complete handle_command(command) with a match statement on the string: 'go' returns 'moving'; 'look' or 'inspect' returns 'describing'; 'quit', 'exit' or 'q' returns 'bye'; anything else returns 'unknown'.
def handle_command(command):
    # match command against literal patterns
    pass

Run your code to check it against the tests.

Exercise 2

+35 XP
Complete parse_move(words), which takes a command already split into a list of words and uses match to return a string:
  • [] gives 'empty'
  • 'drop' followed by any number of items gives 'drop ' plus the items joined with ', ', or 'drop none' when there are no items
  • any other single word, such as ['look'], gives 'single: look'
  • any other two words, such as ['go', 'north'], gives 'move go north'
  • anything else gives 'unparsed'
Cases are tried from the top, and the first one that matches wins.
def parse_move(words):
    # match on the list of words
    pass

Run your code to check it against the tests.

Exercise 3

+35 XP
Complete describe_event(event) using match, where the first matching case wins:
  • a dictionary with 'type': 'move' and keys 'x' and 'y', where both values are ints, gives f'move to ({x},{y})' (use an if guard to check the ints)
  • any other dictionary with a 'type' key gives f'type {t}'
  • an int below zero gives 'negative number' (a guard again)
  • anything else gives 'other'
def describe_event(event):
    # mapping patterns for the dictionaries, guards for the checks
    pass

Run your code to check it against the tests.