Back to Blog
Python

Python OR Pattern: Combining Match Cases

python or pattern: Learn how to use the OR pattern in Python's match statement to combine alternative cases with the | operator, including syntax, examples, and common...

Pythonpattern matchingmatch statementOR operatorstructural pattern matching
Illustration of Python OR pattern combining two match cases with a vertical bar symbol

python or pattern requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.

When you need a match statement to handle several distinct values with the same action, the OR pattern lets you combine those cases into one line. Instead of writing separate case clauses for each value, you can join them with a vertical bar (|) and share the same body. This keeps the code concise and makes the intent explicit.

match command: case "start" | "run": launch() case "stop" | "halt": shutdown()

Here, "start" and "run" both trigger launch(), while "stop" and "halt" trigger shutdown(). The OR pattern is a core part of Python's structural pattern matching, introduced in Python 3.10, and it works with any pattern that can be matched against a subject.

Basic Syntax of the OR Pattern

The OR pattern is written by placing | between two or more patterns. The whole group is treated as a single case. The subject is matched against each alternative in order, and the first one that succeeds determines the outcome.

match value: case 0 | 1: print("binary digit") case 2 | 4 | 6 | 8: print("even digit") case _: print("other")

There is no limit to the number of alternatives you can chain. The syntax is identical to a boolean OR, but in this context it means "match any one of these patterns."

Matching Literal Values with OR

The most common use is to combine literal values—strings, numbers, booleans, or None. This is useful for mapping several distinct inputs to the same behavior.

def handle_status(status): match status: case 200 | 201 | 204: print("success") case 400 | 404 | 500: print("error") case _: print("unknown")

Each alternative must be a valid pattern. For literals, this means the value itself. You cannot use a variable name directly as an alternative because a bare name is treated as a capture pattern, not a value comparison. To match a variable's value, you need to use a value pattern, which is covered later.

Combining Class Patterns

OR patterns are not limited to literals. You can combine class patterns to match different types or different attribute values in one case.

class Point: def __init__(self, x, y): self.x = x self.y = y match obj: case Point(x=0, y=0) | Point(x=0, y=1): print("origin or adjacent") case Point(x=0, y=_): print("on y-axis") case _: print("other")

When combining class patterns, each alternative must bind the same set of variables if you want to use them in the body. If one alternative binds x and another binds y, the match will fail at runtime because the pattern is not consistent. The Python documentation explicitly states that all alternatives must bind the same names.

Using OR with Capture Patterns and Guards

A capture pattern (a bare name) binds the matched value to a variable. When you combine a capture pattern with an OR, the variable is bound only if all alternatives bind the same variable. For example:

match value: case [x] | [x, _]: print(f"one or two elements, first is {x}")

Here, both alternatives bind x to the first element, so the body can use x. If one alternative did not bind x, the pattern would be invalid.

Guards can be applied to the entire OR pattern. The guard is evaluated only after the pattern itself matches, and it applies to whichever alternative succeeded.

match point: case (x, y) if x > 0 | y > 0: print("at least one coordinate is positive")

Be careful with operator precedence. The guard expression is evaluated as a whole; x > 0 | y > 0 is not the same as (x > 0) or (y > 0). In fact, | has higher precedence than >, so this expression would be interpreted as x > (0 | y) > 0, which is almost certainly not what you want. Always use parentheses in guards when combining conditions.

Common Pitfalls and Limitations

One frequent mistake is trying to use a variable as a literal alternative. For instance:

allowed = "start" match command: case allowed | "run": launch()

This does not compare command to the value of allowed. Instead, allowed is treated as a capture pattern that binds the entire subject, so the case always matches. To match against a variable's value, use a value pattern with a dotted name or a literal in the alternative. You can work around this by using a guard:

allowed = "start" match command: case value if value == allowed or value == "run": launch()

Another limitation is that OR patterns cannot be nested inside capture patterns. For example, case [x | y] is invalid because a capture pattern cannot contain an OR pattern. You must restructure the logic or use a guard.

Performance and Maintainability Considerations

OR patterns do not add runtime overhead beyond the individual pattern matches. The match statement tries each alternative in order, so the first alternative that matches wins. If you have many alternatives, the order can affect performance, but for typical use the difference is negligible.

From a maintainability perspective, OR patterns reduce duplication when several values share the same handling. However, if the list of alternatives is long or changes frequently, consider extracting the logic into a function or using a dictionary mapping. For example, a dictionary can map status codes to handlers more flexibly than a long OR pattern.

handlers = { 200: handle_success, 201: handle_success, 204: handle_success, 400: handle_error, }

This approach is easier to extend and avoids the need to modify the match statement itself. Use OR patterns when the alternatives are few and the behavior is straightforward; use a mapping when the set of values is dynamic or large.

When to Prefer Separate Cases Over OR Patterns

Sometimes writing separate case clauses is clearer than combining them with |. If each alternative needs its own guard or binds different variables, separate cases are necessary. For example:

match value: case 0: print("zero") case 1: print("one") case _: print("other")

If the actions are identical and the alternatives are simple literals, OR patterns are more concise. If the actions differ or the logic is complex, separate cases improve readability. The decision depends on how much the alternatives share.

Another scenario is when you need to bind a variable from one alternative but not from another. Since OR patterns require consistent variable binding, separate cases let you handle each alternative individually.

match obj: case Point(x=0, y=0): print("origin") case Point(x=0, y=_): print("on y-axis")

Here, the first case binds no variables, while the second binds y. Combining them would be impossible because the variable sets differ. Separate cases give you the flexibility to bind only what you need.

Finally, remember that OR patterns are a compile-time construct; they do not short-circuit like a boolean or. The match statement evaluates each alternative in order, but the result is the same. Understanding this distinction helps you avoid confusion when reading code that mixes | in patterns and or in guards.

python or pattern: Practical Usage and Code Examples | RYUSLOG DEV