Python Match Case Wildcard: Using _ and as Patterns
Understand Python match case wildcard patterns: `_` matches anything, `as` captures it, and case order decides which pattern wins.
The wildcard pattern in Python's match statement is written as _ and matches any value without binding it to a name. This article explains how _ and as patterns work in practice: when to use them, how ordering affects a match, and where guards fit in.
The Basic Wildcard Pattern
The simplest wildcard is the single underscore _. In a match statement, it matches any value and does not bind it to a variable. This is useful when you need a default case or when you want to ignore a part of a structure.
command = input() match command.split(): case ["quit"]: print("Goodbye") case ["hello", name]: print(f"Hello, {name}") case _: print("Unknown command")
Here, case _ catches any list that does not match the previous patterns. The underscore is not a variable in the pattern; it simply means "match anything". That also means the pattern does not bind _, so inside that case block you cannot rely on _ holding the matched value. If you need the matched value, use a capture pattern instead (shown in the next section).
Capturing Matched Values with as
Sometimes you need to match any value but also keep a reference to it. The as keyword allows you to bind the matched value to a name. This is often called a capturing wildcard.
match value: case int() as number: print(f"Integer: {number}") case str() as text: print(f"String: {text}") case other: print(f"Something else: {other}")
The final case other is a capture pattern: it matches any value and binds it to other. When used as the last case, it behaves like a catch-all. It is equivalent to case _ as other because the wildcard always matches and the as part binds the value. Note that case other is technically a capture pattern, not a wildcard, but it is often described as a capturing wildcard because it catches anything not matched earlier.
Ordering Matters: Wildcards as Default Cases
A wildcard pattern will match any value, so it must be placed last if you want earlier patterns to get a chance. Python evaluates cases in order and uses the first one that matches. If you put a wildcard first, it will always match, and later cases become unreachable.
match status: case _: print("Default") case 200: print("OK")
This code always prints Default because _ matches everything. The case 200 is never evaluated. To avoid this, put the wildcard last. This is a correctness issue, not a performance one: Python follows the order you write and does not move an obvious default to the end for you.
Sequence Patterns and Mapping Patterns
Wildcards can appear inside sequence patterns to ignore parts of a structure. For example, to match a list where the first element is "config" and ignore the rest:
match data: case ["config", *_]: print("Configuration found") case ["data", first, *_]: print(f"First data item: {first}")
The *_ pattern matches zero or more elements and does not bind them. Mapping patterns do not have a **_ wildcard. To allow extra keys, use a **rest capture, which binds the remaining keys to a name:
match request: case {"method": "GET", **rest}: print("GET request") case {"method": "POST", "body": body, **rest}: print(f"POST body: {body}")
Here, **rest captures any keys not explicitly named. Use it when you only care about specific fields. If the extra keys are not needed, the captured rest variable is simply unused.
Combining Wildcards with Guards
A guard is an if condition attached to a case. You can use a wildcard pattern with a guard to create a conditional default. This is useful when you want to match any value but only under certain conditions.
match value: case _ if value is None: print("No value") case _ if value == "": print("Empty string") case _: print("Something else")
In this example, the first two cases use a wildcard with a guard. They match any value, but the guard restricts when they apply. The final case _ is the unconditional default. Guards are evaluated only after the pattern matches, so a wildcard with a guard is a precise way to filter values without binding them.
Performance and Maintainability Considerations
Pattern matching is order-sensitive: each case is tested in source order until a match is found. For long chains, try grouping related patterns or using dictionaries for dispatch.
From a maintainability perspective, wildcards improve readability when you have many distinct cases and a known default. They also make it clear that a default behavior exists. However, overusing wildcards can hide bugs if you accidentally match unexpected values. Prefer explicit patterns for known cases and reserve wildcards for genuinely unknown input.
Compatibility is a key constraint: the match statement was introduced in Python 3.10. If your code must run on older versions, you cannot use it. For projects that require Python 3.9 or earlier, stick to if-elif-else chains. When you do adopt pattern matching, use wildcards judiciously to keep the logic transparent.
When to Prefer Wildcards Over if-elif-else
Wildcards in match are not always the best choice. They shine when you are destructuring complex nested data, such as parsing command-line arguments or processing JSON payloads. In those cases, a wildcard inside a sequence pattern, or a **rest capture inside a mapping pattern, avoids verbose manual indexing and type checks. For simple scalar comparisons, an if-elif-else chain is often clearer and more familiar to other developers.
Consider the tradeoff: pattern matching gives you concise destructuring and guards, but it introduces a new syntax that some team members may not know. If your team is comfortable with Python 3.10+, the wildcard pattern is a powerful tool. If not, stick to traditional branching. The decision should be based on the structure of your data and the team's familiarity with the feature.