Python as Pattern: Using `as` in Match Statements
python as pattern: Learn how the `as` pattern in Python's match statement binds names to subjects, enabling cleaner and more readable pattern matching code.
When you write a match statement in Python, you often need both the destructured components and the original subject. The as pattern solves this by binding the entire matched subject to a name. This article explains how to use python as pattern effectively, with examples across sequence, mapping, and class patterns.
The as Pattern in a Minimal Example
Consider a function that inspects a list and needs both the first two elements and the whole list:
def describe_list(items): match items: case [first, second] as whole: return f"Two items: {first} and {second}; whole list: {whole}" case _: return "Not a two-item list"
Here, as whole captures the entire items object while first and second are bound to the first two elements. Without as, you would need to reference items directly inside the case body, which is less explicit and can be error-prone if the subject expression is complex.
The as keyword works with any pattern, not just sequences. It binds the subject that the pattern is matched against, making it available under the given name.
How the as Pattern Binds Names
The name introduced by as is local to the case block. It is assigned only if the pattern matches. The binding follows normal Python scoping rules: the name is not visible outside the match statement.
match value: case int() as number: print(number) # number is the original int case _: pass
In this example, number receives the same object as value when value is an int. This is useful when you want to apply additional logic to the whole subject after destructuring parts of it.
The name binding happens after the pattern is successfully matched. If the pattern fails, the name is not bound, and the next case is evaluated.
Using as with Different Pattern Types
The as pattern composes with all pattern types. Here are three common combinations.
Sequence Patterns
def process_pair(seq): match seq: case [x, y] as pair: return (x + y, pair) case _: return None
pair refers to the original sequence, which could be a list, tuple, or any sequence that supports pattern matching. This avoids re-indexing or slicing.
Mapping Patterns
def extract_config(config): match config: case {"host": host, "port": port} as full_config: return (host, port, full_config) case _: return None
Here, full_config is the entire dictionary, even if it contains extra keys beyond host and port. This is particularly useful when you need to pass the whole configuration object to another function.
Class Patterns
class Point: def __init__(self, x, y): self.x = x self.y = y def describe_point(p): match p: case Point(x=0, y=0) as origin: return f"Origin at {origin}" case _: return "Not the origin"
The as pattern binds the entire Point instance, allowing you to call methods or access attributes that were not part of the pattern.
Combining as with Wildcards and Guards
The as pattern works alongside wildcards and guards. For example, you can bind the subject even when using _ as a wildcard:
match data: case [_, _] as two_items: print(two_items)
This matches any two-element sequence and binds the whole sequence to two_items. You can also combine as with a guard:
match point: case Point(x, y) as p if x > 0 and y > 0: return p
The guard is evaluated after the pattern matches and the bindings are made, so p is available in the guard expression.
Common Mistakes and Pitfalls
One frequent mistake is using as with a name that already exists in the enclosing scope. The case block will shadow the outer variable, which can lead to subtle bugs:
name = "default" match value: case str() as name: print(name) # shadows outer name
If you need the original variable later, use a different name or avoid as altogether.
Another pitfall is assuming that as binds only the part of the pattern that is explicitly matched. In reality, it always binds the entire subject, regardless of how much of the subject the pattern consumes. For example:
case [1, *rest] as whole: # whole is the full list, not just the part after 1
This is intuitive once you understand the the binding refers to the subject, not the sub-pattern.
Finally, remember that as is not allowed in a pattern that already uses a capture with the same name. For instance, case [x] as x: is a syntax error because x is used both as a capture and as the as target.
When to Use the as Pattern
Use as when you need to refer to the entire subject after destructuring. This is common when you want to pass the whole object to a helper function or log it for debugging. It also improves readability by giving the subject a meaningful name within the case block.
Consider this alternative without as:
match items: case [first, second]: return (first + second, items)
Here, items is referenced directly. This works, but if the the subject expression is long or complex, repeating it inside the case body is error-prone. The as pattern keeps the code self-contained and avoids accidental re-evaluation of the subject.
Maintainability and Performance Considerations
The as pattern has no runtime performance cost beyond a simple name binding. It does not copy the subject; it merely creates a reference. The main benefit is maintainability: it makes the code's intent clearer and reduces the chance of mistakes when the subject is a complex expression.
When the subject is a function call or a property access, using as avoids evaluating that expression multiple times. For example:
match get_config(): case {"debug": True} as config: # config is the result of get_config()
Without as, you would need to call get_config() again or store it in a separate variable before the match. The as pattern integrates this cleanly into the pattern-matching flow.
In terms of maintainability, prefer as when the subject is used more than once in the case body. If you only need the destructured parts, skip as to avoid an unnecessary binding. Overusing as can clutter the case if the bound name is not actually used.
Finally, be aware that the as pattern is available only in Python 3.10 and later, since that is when structural pattern matching was introduced. If your codebase must support older versions, you cannot use match statements at all, so this pattern is not applicable. For modern Python projects, as is a valuable tool for writing concise and expressive pattern-matching logic.