Why does `case RED:` in a Python match statement rebind RED instead of comparing against it?
answer
- The left of a case is not an expression
- Some patterns can never fail
- Binding, not comparing
- A dot changes the meaning entirely
- Colour.RED versus RED
basics
~20 sA bare name in a case is a capture pattern: it always matches and binds the subject to that name, comparing nothing. To compare against a named constant you must write a dotted value pattern such as Color.RED.
solid answer
~40 sIn the pattern grammar added in Python 3.10 (PEP 634), a pattern is not an expression. A single undotted name other than `_` is a **capture pattern**: it is irrefutable — it matches any subject — and it binds the subject to that name, so `case RED:` overwrites `RED` and fires for everything. Comparing against a named constant requires a **value pattern**, which the grammar defines as a *dotted* name: `case Color.RED:` looks the attribute up at match time and compares it with `==`. The dot is the whole difference. CPython rejects the mistake at compile time whenever the capture is followed by another case, with a name-capture error saying the remaining patterns are unreachable; it stays silent only when the bare-name case is the last one.
code
python · 8 linesRED = "red"
colour = "green"
match colour:
case RED:
print("matched, RED is now", RED)
print("module constant:", RED)go deeper
Recall the one-line rule: a bare name after case binds and always matches, a dotted name compares. Be able to spot case RED: in a snippet and say what the branch will actually do.
Explain the mechanics: capture patterns are irrefutable, they assign with normal scoping rules, and value patterns do an attribute lookup at match time compared with ==. Know that CPython raises a name-capture SyntaxError when the capture is not the last case.
Show the production angle: the silent form is a final bare-name case, so a dispatch table can quietly route everything to one branch. Be ready to describe how you would confirm it and why an enum is the structural fix.
Own the convention. Decide that constants used in dispatch live on enums rather than as loose module names, make 'a case name without a dot binds' a review rule, and weigh whether pattern matching earns its keep over dict dispatch in your codebase.
### Patterns are not expressions `match` / `case` arrived in Python **3.10** (PEP 634). The thing after `case` is a **pattern**, and a pattern is a distinct grammatical construct — it is not an expression that gets evaluated and compared. The grammar recognises a handful of pattern kinds: literal patterns, the wildcard `_`, **capture patterns**, **value patterns**, group patterns, and the structural sequence, mapping and class patterns. Capture and value patterns look almost identical on the page, and that near-identity is the single most common `match` bug. ### A bare name is a capture pattern A pattern consisting of one plain name — anything that is not `_` and contains **no dot** — is a capture pattern. It does two things: it **always matches** (it is *irrefutable*, it cannot fail for any subject), and it **binds** the subject to that name using ordinary assignment rules. Inside a function the name becomes a local; at module level it rebinds the global. Nothing is compared. So: ```python RED = "red" colour = "green" match colour: case RED: print(RED) # green -- RED now names the subject ``` The case fires for `"green"`, for `None`, for a list, for anything, and the constant `RED` is gone. ### Why the language was designed this way PEP 635 explains the trade. Destructuring is the *point* of `match`, so binding sub-values has to be the cheap, unmarked spelling — a syntax like `case Point(x=$x)` was rejected as noise. That leaves the question of how to *compare* against a named constant, and the answer is to require a qualifier: a **value pattern** is a dotted name such as `Color.RED`, `Level.WARN` or `limits.MAX`. The dot is what tells the compiler "load this and compare it" rather than "store into this". Making the distinction syntactic — rather than, say, resolving it by whether the name happens to be defined — means the meaning of a case never depends on what else is in scope, and a typo can never silently flip a comparison into a capture that quietly swallows every subject. ### The compiler catches most of the mistake for you An irrefutable pattern makes every later case unreachable, so CPython rejects it at **compile time**: ```python match value: case RED: # SyntaxError: name capture 'RED' makes remaining patterns unreachable ... case GREEN: ... ``` That guard covers the majority of real occurrences — which is why the trap is famous but usually loud. It stays silent in exactly one shape: when the bare-name case is the **last** case in the statement. Then there is nothing left to make unreachable, the module compiles, and the branch simply swallows everything that reached it. The same silence applies to the sole case of a one-case `match`. ### What a value pattern actually does `case Color.RED:` performs the attribute lookup **at match time**, every time that case is tried, and compares the loaded object to the subject with `==`. It never binds anything. Two practical consequences follow: the pattern is not frozen at compile time, so if the attribute is rebound between calls the matching changes with it; and the comparison respects whatever `__eq__` the objects define. For `enum.Enum` members that comparison is identity-based, because `Enum` does not define `__eq__`, which is why enums are the idiomatic home for constants used in `match`. ### The three ways to write the constant * **An enum** — `class Level(enum.Enum): OK = 0; WARN = 1` and then `case Level.WARN:`. Self-documenting, gives you the dot for free, and members compare by identity. * **A module** — `import limits` (not `from limits import MAX`) and then `case limits.MAX:`. * **A namespace object** — constants hung on a small class or on `types.SimpleNamespace`, then `case Thresholds.WARN:`. There is no way to write a bare constant name and have it compare; the grammar has no escape hatch, and a subscript such as `case table["max"]:` is a syntax error too. ### Binding is not conditional on the case winning Bindings made while a pattern is being tried are **not** rolled back when the pattern later fails. A compound pattern can bind its first few names and then fail on a later sub-pattern, leaving those names assigned. That is why you should not read a capture name outside the branch that matched, and why the names bound by a `match` outlive the statement in exactly the way a `for` loop variable does. ### The one name that does not capture `_` is the **wildcard**: it always matches like a capture pattern but binds nothing, which is what makes `case _:` the conventional catch-all. ### How this shows up in an interview The question is usually posed as a small snippet with a module-level constant and a `case` that names it, and the expected answer has three parts: the case is a capture pattern, it always matches and rebinds the name, and the fix is to give the constant a dotted path — normally by making it an `enum.Enum` member. A candidate who adds that CPython refuses to compile the non-final form, with a name-capture error, is showing they have actually hit it.
- What does CPython do if a bare-name case is followed by more cases?It refuses to compile the module. An irrefutable pattern makes everything after it unreachable, so the compiler raises a SyntaxError naming the captured name and saying the remaining patterns are unreachable. That guard is why the trap is usually loud; it goes silent only when the bare-name case is the last case in the statement, or the only one.
- How do you compare against a module-level constant that has no natural namespace?Give it a dot. Either import the module rather than the name — `import limits` then `case limits.MAX:` — or promote the constants to an `enum.Enum` and match on members, or hang them on a small class or a `types.SimpleNamespace`. The value-pattern grammar requires at least one dot, and a subscript such as `case table["max"]:` is a syntax error.
- Is the dotted name in a value pattern resolved once, or on every match?On every attempt. `case limits.MAX:` performs the attribute lookup at match time, each time that case is tried, and compares the result to the subject with `==`. Nothing is frozen at compile time, so rebinding the attribute between calls changes what the case matches — which is another reason to prefer immutable enum members for constants used in a match.
A bare name in a case is a labelled box, not a photograph to compare against: Python drops whatever arrived into the box instead of checking whether it looks like what used to be there.
saying these in an interview costs you the question
- Says `case RED:` compares the subject to the constant RED
- Thinks a capture pattern can fail to match some subjects
- Believes a bare name and the wildcard `_` behave identically
- Expects a runtime warning instead of a compile-time error
- Claims quoting or uppercasing the name makes it compare
- Says the constant must be redefined inside the match