The Walrus Operator

500px-Noaa-walrus22.jpg

Let’s suppose that we need to process a set of instructions to a robot. The instructions are in a file, one per line, and they are of two kinds:

Our goal is to read a file containing such instructions and determine where the robot is after executing them. The robot starts at the origin (0,0), facing east (i.e. along to positive x-axis).

Let’s begin by writing a function that outputs such a list of instructions at random.

from random import seed, randint

seed()


def random_instructions(count=8, unit_max=8):
    """
    Write a number of robot instructions given by count.  The turns are in
    multiples of 10 degrees.
    """
    for i in range(count):
        if 0 == randint(0, 1):
            units = randint(1, unit_max)
            print(f"MOVE {units:4d}")
        else:
            degrees = 10 * randint(-18, 18)
            print(f"TURN {degrees:4d}")

Let’s try it out.

random_instructions()
TURN  -80
MOVE    5
MOVE    7
TURN  -30
TURN -180
MOVE    3
TURN   30
TURN  180

Now let’s write some instructions to a file.

from contextlib import redirect_stdout

INSTRUCTION_FILE = "/tmp/robot-instructions.txt"

with open(INSTRUCTION_FILE, "w") as rif:
    with redirect_stdout(rif):
        random_instructions(16)

Now we turn to reading the instructions.

def follow_instructions(filename):
    with open(filename) as insts:
        for line in insts.readlines():
            print(line, end="")


follow_instructions(INSTRUCTION_FILE)
TURN  -80
TURN  140
MOVE    7
TURN    0
TURN  130
MOVE    8
TURN  -50
MOVE    3
MOVE    5
TURN  180
MOVE    8
MOVE    1
TURN   30
MOVE    4
MOVE    6
MOVE    5

Note how each line is read including its final newline. The print statement then prints the line without adding another newline.

def follow_instructions(filename):
    with open(filename) as insts:
        for line in insts.readlines():
            print(line, end="")


follow_instructions(INSTRUCTION_FILE)
TURN  -80
TURN  140
MOVE    7
TURN    0
TURN  130
MOVE    8
TURN  -50
MOVE    3
MOVE    5
TURN  180
MOVE    8
MOVE    1
TURN   30
MOVE    4
MOVE    6
MOVE    5

There are several ways we could now continue but we chose to use regular expressions so that we can demonstrate the walrus operator.

from re import compile, match

RE_MOVE = compile(r"MOVE +(\d+)")
RE_TURN = compile(r"TURN +(-?\d+)")


def follow_instructions(filename):
    with open(filename) as insts:
        for line in insts.readlines():
            if m := match(RE_MOVE, line):
                print("moving", m.group(1), "units")
            elif m := match(RE_TURN, line):
                print("turning", m.group(1), "degrees")
            else:
                print("Failed to parse:", line)


follow_instructions(INSTRUCTION_FILE)
turning -80 degrees
turning 140 degrees
moving 7 units
turning 0 degrees
turning 130 degrees
moving 8 units
turning -50 degrees
moving 3 units
moving 5 units
turning 180 degrees
moving 8 units
moving 1 units
turning 30 degrees
moving 4 units
moving 6 units
moving 5 units

Notice how match(RE_MOVE, line) tries to match the MOVE regular expression against line. If it fails it returns None and the if statement fails. However if it succeeds it returns a match object which makes the if statement succeed. The walrus operator := captures the result of the match in the variable m. This can then be used in the body of the if statement.

Now let’s fill in the actual actions. Note that the trigonometric operations in Python’s math library take their arguments in radians so we, write functions that take their arguments in degrees.

from math import pi, cos, sin


def d2r(d):
    return pi * d / 180


def cosd(d):
    return cos(d2r(d))


def sind(d):
    return sin(d2r(d))


def follow_instructions(filename, location=None, orientation=0):
    if not location:
        location = [0, 0]
    with open(filename) as insts:
        for line in insts.readlines():
            if m := match(RE_MOVE, line):
                units = int(m.group(1))
                location[0] += units * cosd(orientation)
                location[1] += units * sind(orientation)
                print(f"MOVE {units:4d}")
            elif m := match(RE_TURN, line):
                degrees = int(m.group(1))
                orientation += degrees
                print(f"TURN {degrees:5d}")
            else:
                print("Failed to parse:", line)
            print(f"{orientation:4d} ({location[0]:4.2f}, {location[1]:4.2f})")
    print(location)


follow_instructions(INSTRUCTION_FILE)
TURN   -80
 -80 (0.00, 0.00)
TURN   140
  60 (0.00, 0.00)
MOVE    7
  60 (3.50, 6.06)
TURN     0
  60 (3.50, 6.06)
TURN   130
 190 (3.50, 6.06)
MOVE    8
 190 (-4.38, 4.67)
TURN   -50
 140 (-4.38, 4.67)
MOVE    3
 140 (-6.68, 6.60)
MOVE    5
 140 (-10.51, 9.82)
TURN   180
 320 (-10.51, 9.82)
MOVE    8
 320 (-4.38, 4.67)
MOVE    1
 320 (-3.61, 4.03)
TURN    30
 350 (-3.61, 4.03)
MOVE    4
 350 (0.33, 3.34)
MOVE    6
 350 (6.24, 2.29)
MOVE    5
 350 (11.16, 1.43)
[11.159698714204431, 1.4254821304651173]

If we want to check this more carefully we could write a set of tests but we can gain some confidence by making some simple sets of instructions.

def random_instructions(count=8, unit_max=8):
    """
    Write a number of robot instructions given by count.  The turns are in
    multiples of 90 degrees.
    """
    for i in range(count):
        if 0 == randint(0, 1):
            units = randint(1, unit_max)
            print(f"MOVE {units:4d}")
        else:
            degrees = 90 * randint(-4, 4)
            print(f"TURN {degrees:4d}")


with open(INSTRUCTION_FILE, "w") as rif:
    with redirect_stdout(rif):
        random_instructions(4)

follow_instructions(INSTRUCTION_FILE)
TURN    90
  90 (0.00, 0.00)
TURN   -90
   0 (0.00, 0.00)
MOVE    7
   0 (7.00, 0.00)
MOVE    8
   0 (15.00, 0.00)
[15.0, 0.0]

Author: Breanndán Ó Nualláin <o@uva.nl>

Date: 2026-04-23 Thu 15:49