skip to content

In a Python match statement, how does a literal pattern compare the subject to the case value?

level: middleimportance: should knowfreq 35%

answer

  1. Not every literal is checked the same way
  2. Two different operators are involved
  3. Three values are special-cased
  4. Booleans are integers too
  5. `case 1:` and the subject True

basics

~10 s

Literal patterns compare with ==, except None, True and False, which are compared with is. So case 1: also matches True and 1.0, while case True: matches only the True object.

solid answer

~40 s

PEP 634 gives literal patterns two rules. `None`, `True` and `False` are matched with **`is`**, because they are singletons and identity is immune to a subject class with a permissive `__eq__`. Every other literal — ints, floats, complex, `str`, `bytes` — is matched with **`==`**. The consequences are the interview material: `bool` subclasses `int` and `True == 1`, so `case 1:` matches the subject `True`, while `case True:` never matches the integer `1`; `case 1:` also matches `1.0` and anything else that compares equal to 1. And because `str` and `bytes` never compare equal, `case "ok":` silently fails against `b"ok"` — with no error, since an unmatched `match` simply does nothing.

code

python · 12 lines
python
def kind(x):
    match x:
        case True:
            return "the True singleton"
        case 1:
            return "equal to 1"
        case None:
            return "the None singleton"
        case _:
            return "other"

print(kind(True), "|", kind(1), "|", kind(1.0), "|", kind(None))

go deeper

for a junior

Recall that a literal after case is a real test that can fail, unlike a bare name. Knowing that None is matched by identity and numbers and strings by equality is enough at this level.

for a middle

State both rules precisely and give a consequence unprompted: case 1: matches True because bool subclasses int, and case True: does not match 1 because it uses identity. Explain why case order then matters.

for a senior

Bring the failure mode: a match over data read in binary mode can silently match nothing, because str and bytes never compare equal and an unmatched match is a no-op. Say how you would surface it with a catch-all that raises.

for a principal

Frame the guidance: literal-heavy dispatch is fragile across type boundaries, so normalise inputs before the match and decide as a team whether pattern matching or a mapping is the clearer dispatch mechanism for the codebase.

### What a literal pattern is In a `match` statement (Python **3.10**, PEP 634) a **literal pattern** is a case written as a literal value: a number, a string, a bytes literal, or one of `None`, `True`, `False`. Complex forms such as `case 3 + 4j:` and signed numbers such as `case -1:` are allowed because the grammar special-cases them; an f-string is not a valid literal pattern. Unlike a capture pattern (a bare name, which always matches and binds) a literal pattern is a genuine test that can fail. ### Two comparison rules, not one The rule interviewers are probing is that literal patterns do **not** all compare the same way: * `None`, `True` and `False` are compared with **`is`**. * Every other literal — ints, floats, complex, `str`, `bytes` — is compared with **`==`**. The three singletons get the identity treatment for the same reason style guides insist on `x is None`: they are singletons, so identity is both correct and immune to a subject class that has redefined `__eq__` to be sloppy. Everything else uses `==` because value equality is what you actually mean when you write `case 404:` or `case "GET":`. ### The consequences that catch people out **`bool` is a subclass of `int`, and `True == 1`.** So a literal pattern `case 1:` *does* match the subject `True`, and `case 0:` matches `False`. Going the other way it does not: `case True:` uses `is`, so it matches only the `True` object and never the integer `1`. Case order therefore decides the answer: ```python match value: case True: ... # only the True object case 1: ... # 1, 1.0, True if True was not caught above ``` **Numeric equality crosses types.** `1 == 1.0` is true, so `case 1:` matches `1.0`, and it matches any object that compares equal to 1 — a `decimal.Decimal("1")`, or `fractions.Fraction(1, 1)`. If you need the type as well as the value, that is a class pattern's job, not a literal's. **`str` and `bytes` never compare equal.** `case "ok":` will not match the subject `b"ok"`, and `case b"ok":` will not match `"ok"`. A `match` over data that came off a socket or a file opened in binary mode is a classic place for every case to silently fail; decode first, or write bytes literals. **The subject's `__eq__` is in charge.** Because the comparison is `==`, a subject whose class defines a permissive `__eq__` can match a literal you did not expect. The `is` rule for the three singletons is precisely what stops that from happening to `case None:`. ### What happens when nothing matches A literal pattern that fails just falls through to the next case; if no case matches at all, the `match` statement does nothing — no error, no warning. Combined with the `str`/`bytes` gap above, that is how a whole dispatch table can become a no-op without anyone noticing. ### How it fits with the other pattern kinds Reading a `case` line, three shapes have three different meanings, and telling them apart is most of what `match` fluency is: * `case 1:` — literal pattern, compared with `==`. * `case x:` — capture pattern, always matches, binds `x`. * `case limits.MAX:` — value pattern, attribute looked up at match time and compared with `==`. Only the dotted form compares a *named* constant. There is no spelling that lets a bare name compare, and a subscript such as `case table["max"]:` is a syntax error. Something that looks like a call — `case Point(0, 0):` — is parsed as a class pattern, not evaluated as an expression. ### A worked example ```python def kind(x): match x: case True: return "the True singleton" case 1: return "equal to 1" case None: return "the None singleton" case _: return "other" kind(True) # 'the True singleton' -- is kind(1) # 'equal to 1' -- == kind(1.0) # 'equal to 1' -- == crosses int/float kind(None) # 'the None singleton' -- is ``` Swap the first two cases and `kind(True)` becomes `'equal to 1'`, which is the whole lesson in one edit. ### Interview framing A strong answer states both rules in one breath — `==` for ordinary literals, `is` for `None`, `True` and `False` — then supplies one consequence unprompted, usually that `case 1:` matches `True`. Mentioning that the singleton rule exists to make `case None:` immune to a redefined `__eq__` is the detail that separates someone who has read PEP 634 from someone who has memorised a table.

  • Why does swapping `case True:` and `case 1:` change which branch a True subject takes?
    Cases are tried top to bottom and the first match wins. `case True:` uses `is`, so it claims the True object only. `case 1:` uses `==`, and `True == 1`, so if it comes first it claims True as well and the later `case True:` becomes dead for that subject. Order is part of the semantics, not a style choice.
  • Why did PEP 634 special-case None, True and False to use `is`?
    They are singletons, so identity is exactly the right test, and it mirrors the long-standing convention of writing `x is None`. It also makes `case None:` immune to a subject whose class defines a permissive `__eq__` that would otherwise compare equal to None. Ordinary literals keep `==` because value equality is what `case 404:` or `case "GET":` actually means.
  • Can a case pattern contain an arbitrary expression, such as a subscript into a constants table?
    No. Patterns are their own grammar: literals, the wildcard, capture names, dotted value patterns, and the structural forms. `case table["max"]:` is a syntax error, and something that looks like a call, such as `case Point(0, 0):`, is parsed as a class pattern rather than evaluated. If you need a computed value, compute it before the match and match on a dotted name.

saying these in an interview costs you the question

  • Says every literal pattern compares with `is`
  • Says every literal pattern compares with `==`, including None
  • Insists `case 1:` cannot match the subject True
  • Believes `case "ok":` matches the bytes b"ok"
  • Thinks an unmatched match statement raises an error
  • Assumes int and float literals can never cross-match

context