PythiloLesson 25 of 30 · progress kept in this browser

Pattern matching

match tries each case from top to bottom and runs the first one that fits. A pattern can test shape and type and pull out parts in the same step.

def describe(cmd):
    match cmd:
        case ["go", direction]:
            return f"going {direction}"
        case ["take", *items] if items:
            return f"taking {len(items)}"
        case {"say": str(text)}:
            return f"saying {text}"
        case int() | float() as n:
            return f"number {n}"
        case _:
            return "unknown"

print(describe(["go", "north"]))
print(describe(["take", "a", "b"]))
print(describe({"say": "hi", "to": "x"}))
print(describe(3.5), describe("go"))
  • A sequence pattern checks the length, and *rest collects the remainder. Strings never match sequence patterns.
  • A mapping pattern checks only the keys it names, so extra keys are fine.
  • str(text) checks the type and binds the value. Point(x=0, y=y) matches the attributes of an object.
  • | tries alternatives, if adds a guard, and as names the whole match.
  • A bare name always matches and binds. To compare with a constant, use a literal or a dotted name such as Color.RED.
  • case _: catches everything else.
Your task

Write evaluate(expr, env) for small expression trees: an int or float is itself, ("neg", x) is minus x, ("add", a, b) and ("mul", a, b) combine their evaluated parts, and {"var": name} is env[name]. Anything else raises ValueError.

Next: Decorators