Why must every alternative of a `match` or-pattern bind the same names?
answer
- One body shared by all alternatives
- The body must not face an unbound local
- The compiler checks it, not the runtime
- The wildcard binds nothing and pads shapes
basics
~20 sBecause the case body must be able to read every captured name no matter which alternative matched, and Python has no maybe-unbound state. The compiler enforces it, raising SyntaxError: alternative patterns bind different names before the code ever runs.
solid answer
~40 sIn a `case` clause, `|` joins alternative patterns: `case [x] | (x,):`. Whichever alternative matches, the same case body runs, so that body must be able to use every capture name unconditionally — Python has no notion of a conditionally-bound local in this position. The compiler therefore requires the set of capture names to be identical across all alternatives and rejects anything else at compile time with `SyntaxError: alternative patterns bind different names`. The wildcard `_` binds nothing, so it is the standard way to pad an alternative to the right shape without adding a name. A second, related rule: an irrefutable alternative such as a bare capture name may only appear last, otherwise you get `SyntaxError: name capture 'a' makes remaining patterns unreachable`.
code
python · 8 linesbad = "match x:\n case [a] | (b,):\n pass\n"
try:
compile(bad, "<demo>", "exec")
except SyntaxError as exc:
print(exc.msg) # alternative patterns bind different names
good = "match x:\n case [y, _] | (y,):\n pass\n"
print(compile(good, "<demo>", "exec") is not None) # Truego deeper
Know that | joins alternatives in one case clause and that case 401 | 403: is the idiomatic way to handle several constants together. Recall that capturing names inside alternatives comes with a restriction.
Explain the rule and its reason: one shared body means every capture must be unconditionally bound, so the compiler demands an identical name set and reports alternative patterns bind different names. Mention _ as the padding escape hatch.
Demonstrate the judgment of when an or-pattern is the wrong tool — if the alternatives want different names or different bodies, they are separate cases. Know that ordering is observable when alternatives overlap.
Own the codebase-level guidance: how much structural cleverness in a single case is worth the readability cost, and when a match over a wide union should be a dispatch table or a class hierarchy instead.
## What an or-pattern is Inside a `case` clause, the vertical bar joins **alternative patterns**. The clause matches if any alternative matches, and alternatives are tried **left to right, first match wins**: ```python match status: case 401 | 403 | 404: return "client error" ``` Or-patterns nest anywhere a pattern is allowed, including sub-positions: `case {"method": "GET" | "HEAD"}:`. ## The same-names rule When alternatives capture, every alternative must bind **exactly the same set of names**: ```python case [x] | (x,): # fine - both bind {x} case [y, _] | (y,): # fine - `_` binds nothing, both bind {y} case [a] | (b,): # SyntaxError: alternative patterns bind different names ``` The reason is straightforward once you look at the case body. All alternatives share **one** body, and the body is compiled once. If `[a] | (b,)` were legal, the body would face two names of which only one is bound on any given run, and a reference to the other would raise `NameError` — or worse, silently read a stale value left over from an earlier iteration. Python has no "possibly bound" local for the compiler to reason about here, so instead of deferring the problem to runtime it rejects the construct outright. This is a **compile-time** error: it fires when the module is compiled, so an unreachable `match` in a rarely-run branch still stops the whole file from importing. Note what the rule does *not* say. It constrains the *names*, not the positions, the types, or the shapes. `case [x, _] | {"v": x} | (x, *_):` is perfectly legal — three completely different shapes, one shared name. ## The wildcard escape hatch `_` is not a capture; it is the wildcard, and it binds nothing at all. That makes it the tool for reconciling alternatives whose shapes differ in arity: ```python case [kind] | [kind, _]: ... # kind is bound either way; the trailing element is discarded ``` If you actually need the extra element, the alternatives are not really alternatives and should be separate `case` clauses, usually with different bodies. ## Ordering of alternatives Because alternatives are tried left to right and the first match wins, order is observable whenever alternatives overlap: ```python match seq: case [x] | [x, *_]: return x ``` For `[9]` the first alternative matches; for `[4, 5, 6]` the first fails and the second binds `x = 4`. Swapping the two alternatives here would not change the result, but it does in general — put the more specific alternative first, exactly as you would with `case` clauses themselves. ## The bar is syntax, not an operator A common misreading is that `|` here is the ordinary binary or-operator being evaluated. It is not. Inside a pattern the bar is grammar: the compiler sees alternatives and emits branching code, and no `__or__` method on the operands is ever called. That matters because the same character means something quite different in an annotation, where `int | str` really does build a union object at runtime. In a `case`, `case 1 | 2:` does not compute `1 | 2` — which would be `3` — it tests the subject against `1`, then against `2`. Alternatives also nest freely in sub-positions: `case [1 | 2, 3 | 4]:` and `case {"method": "GET" | "HEAD"}:` are both legal and are how or-patterns are most often used in practice. ## Irrefutable alternatives must be last A bare capture name matches anything, so an alternative that is just a name makes everything to its right dead code. The compiler says so: ```python case a | [1]: # SyntaxError: name capture 'a' makes remaining patterns unreachable ``` The same applies to `_`, which reports `SyntaxError: wildcard makes remaining patterns unreachable`. Both errors are the or-pattern-level version of the rule that a bare `case _:` must be the final clause of a `match`. ## Guards and `as` around an or-pattern There is one guard per `case`, not one per alternative, and it runs once after whichever alternative matched. An `as` pattern can wrap the whole group so the body gets the matched value under a name — `case ("GET" | "HEAD") as verb if verb in allowed:` — and because `as` binds the same name whatever the alternative was, it satisfies the same-names rule for free. ## In interviews The answer an interviewer wants is the *reason*, not just the rule: one body, so one unconditional set of bindings, enforced at compile time rather than left to blow up as a `NameError` later. Being able to name the wildcard escape hatch and the left-to-right ordering marks a candidate who has actually written or-patterns rather than just read about them. All of this is 3.10 behaviour via PEP 634, unchanged through 3.14.
- How do you write one `case` clause for both a one-element and a two-element sequence when you only care about the first element?Pad the shorter alternative with the wildcard: `case [kind] | [kind, _]:`. `_` binds nothing, so both alternatives bind exactly `{kind}` and the same-names rule is satisfied. If you genuinely need the second element in the body, the two shapes want separate `case` clauses rather than one or-pattern.
- Why does `case a | [1]:` raise a SyntaxError even though both parts look well formed?A bare capture name is irrefutable — it matches anything — so nothing to its right can ever be reached. The compiler rejects it with `name capture 'a' makes remaining patterns unreachable`, the or-pattern equivalent of the rule that a bare `case _:` must be the last clause. Put the irrefutable alternative last, or drop it.
- Does the same-names rule require the alternatives to have the same shape or arity?No. It constrains only the set of capture names. `case [x, _] | {"value": x} | (x, *_):` is legal: three different shapes — sequence, mapping, starred sequence — all binding exactly `{x}`. Shape and type may differ freely as long as the names line up.
saying these in an interview costs you the question
- Thinks the check happens at runtime, not compile time
- Says alternatives must have the same shape or arity
- Believes an unmatched alternative's name is bound to None
- Claims `_` counts as a capture name for the rule
- Thinks alternatives are tried in some optimised order