🌐
Python
peps.python.org › pep-0636
PEP 636 – Structural Pattern Matching: Tutorial | peps.python.org
If the pattern matches but the condition is falsy, the match statement proceeds to check the next case as if the pattern hadn’t matched (with the possible side-effect of having already bound some variables). Your adventure is becoming a success and you have been asked to implement a graphical interface. Your UI toolkit of choice allows you to write an event loop where you can get a new event object by calling event.get(). The resulting object can have different type and attributes according to the user action, for example:
🌐
W3Schools
w3schools.com › python › python_regex.asp
Python RegEx
Python has a built-in package called re, which can be used to work with Regular Expressions. ... You can add flags to the pattern when using regular expressions. A special sequence is a \ followed by one of the characters in the list below, and has a special meaning: A set is a set of characters inside a pair of square brackets [] with a special meaning: The findall() function returns a list containing all matches...
🌐
Plain English Westminster
benhoyt.com › writings › python-pattern-matching
Structural pattern matching in Python 3.10
A critical but informative look at the new structural pattern matching feature in Python 3.10, with real-world code examples.
🌐
Python
peps.python.org › pep-0622
PEP 622 – Structural Pattern Matching | peps.python.org
The match also binds the pattern’s free variable x to the subject’s value 3. As another example, if the subject is [3, 0], the match fails because the subject’s type list is not the pattern’s Point2d.
🌐
W3Schools
w3schools.in › python › regular-expressions
Python Regular Expressions - W3Schools
The re.split() accepts a pattern that specifies the delimiter. Using this, we can match the pattern and separate text data.
🌐
Real Python
realpython.com › structural-pattern-matching
Structural Pattern Matching in Python – Real Python
August 6, 2024 - It can also specify the individual pieces of which an object is made. For example, you may want to define a pattern that will match three-dimensional points that lie on the z-axis:
🌐
Better Stack
betterstack.com › community › guides › scaling-python › python-pattern-matching
Structural Pattern Matching in Python: A Comprehensive Guide | Better Stack Community
Learn how to use structural pattern matching in Python to simplify complex conditionals and make your code more readable. This guide covers matching with basic types, dictionaries, guard clauses, and more—introduced in Python 3.10 and enhanced in Python 3.13.
🌐
Python documentation
docs.python.org › 3 › library › re.html
re — Regular expression operations — Python 3.14.3 ...
Will try to match with yes-pattern if the group with given id or name exists, and with no-pattern if it doesn’t. no-pattern is optional and can be omitted. For example, (<)?(\w+@\w+(?:\.\w+)+)(?(1)>|$) is a poor email matching pattern, which will match with '<user@host.com>' as well as 'user@host.com', but not with '<user@host.com' nor 'user@host.com>'.
🌐
GeeksforGeeks
geeksforgeeks.org › python › pattern-matching-python-regex
Pattern matching in Python with Regex - GeeksforGeeks
June 28, 2017 - (Ha){3, } will match three or more instances of the (Ha) group, while (Ha){, 5} will match zero to five instances. Curly brackets can help make your regular expressions shorter. Example 1: In this example, we will use curly brackets to specify ...
Find elsewhere
🌐
W3Schools
w3schools.com › python › python_match.asp
Python Match
If there is a match, the associated block of code is executed. The example below uses the weekday number to print the weekday name:
🌐
Python Course
python-course.eu › python-tutorial › structural-pattern-matching.php
17. Structural Pattern Matching | Python Tutorial | python-course.eu
We could improve the previous code a tiny little bit, but it should be clear that the match is superior in its clarity in this case. We can further improve our example: What if somebody choses an unknown language or an unknown language plus a region ? It is very easy. Instead of using a string literal in our pattern, we use variable names:
🌐
InfoWorld
infoworld.com › home › software development › programming languages › python
How to use structural pattern matching in Python | InfoWorld
August 9, 2023 - That said, enums tend to be the most familiar and idiomatic way to do this in Python. You cannot match against variable contents through indexing. For instance, case commands[0]: would be rejected as a syntax error. The key to working most effectively with pattern matching is not just to use it as a substitute for a dictionary lookup or an if/else chain.
🌐
Inspired Python
inspiredpython.com › course › pattern-matching › mastering-structural-pattern-matching
Mastering Structural Pattern Matching • Inspired Python
>>> parse_connection(connection) This Connection object talks to example.com · Note that Python’s clever enough to not create instance of Connection during the pattern matching step.
🌐
Python Engineer
python-engineer.com › posts › pattern-matching-python
Master Pattern Matching In Python 3.10 | All Options | - Python Engineer
For example the value 400 here prints only Bad request. In other languages like C++ this would then fall through all cases and also print all other statements, unless we use a break statement. But we don’t need this here in Python. def http_error(status): match status: case 400: print("Bad request") case 401 | 403 | 404: print("Not allowed") A single underscore is used as the wildcard. This means if none of the above patterns matches, the wildcard action is performed.
🌐
Mathspp
mathspp.com › blog › pydonts › structural-pattern-matching-tutorial
Structural pattern matching tutorial | Pydon't 🐍 | mathspp
Every Monday, you'll get a Python deep dive that unpacks a topic with analogies, diagrams, and code examples so you can write clearer, faster, and more idiomatic code. PEP 622 -- Structural Pattern Matching, https://www.python.org/dev/peps/pep-0622/;
🌐
Automate the Boring Stuff
automatetheboringstuff.com › 3e › chapter9.html
Chapter 9 - Text Pattern Matching with Regular Expressions, Automate the Boring Stuff with Python, 3rd Ed
For example, adding a numeral, such as 3, in curly brackets ({3}) after a pattern is like saying, “Match this pattern three times.” So the slightly shorter regex r'\d{3}-\d{3}-\d{4}' also matches the phone number pattern. Note that we often write regex strings as raw strings, with the r prefix.
🌐
Python
peps.python.org › pep-0634
PEP 634 – Structural Pattern Matching: Specification | peps.python.org
This disallows for example case x, x: ... but allows case [x] | x: .... ... A wildcard pattern always succeeds. It binds no name. ... The dotted name in the pattern is looked up using the standard Python name resolution rules. However, when the same value pattern occurs multiple times in the same match statement, the interpreter may cache the first value found and reuse it, rather than repeat the same lookup.
🌐
Python
peps.python.org › pep-0635
PEP 635 – Structural Pattern Matching: Motivation and Rationale | peps.python.org
Pattern matching is complimentary to the object-oriented paradigm. Using OO and inheritance we can easily define a method on a base class that defines default behavior for a specific operation on that class, and we can override this default behavior in subclasses. We can also use the Visitor pattern to separate actions from data. But this is not sufficient for all situations. For example, a code generator may consume an AST, and have many operations where the generated code needs to vary based not just on the class of a node, but also on the value of some class attributes, like the BinOp example above.
🌐
PythonDeck
pythondeck.com › working_with_pattern_matching.php
Mastering Pattern Matching in Python: A Comprehensive Guide
2 days ago - def process_command(command): match command: case 'start': return 'Starting...' case 'stop': return 'Stopping...' case _: return 'Unknown command' In this example, the function checks the value of command and returns a corresponding message.
Top answer
1 of 7
29

Update

I condensed this answer into a python package to make matching as easy as pip install regex-spm,

import regex_spm

match regex_spm.fullmatch_in("abracadabra"):
  case r"\d+": print("It's all digits")
  case r"\D+": print("There are no digits in the search string")
  case _: print("It's something else")

Original answer

As Patrick Artner correctly points out in the other answer, there is currently no official way to do this. Hopefully the feature will be introduced in a future Python version and this question can be retired. Until then:

PEP 634 specifies that Structural Pattern Matching uses the == operator for evaluating a match. We can override that.

import re
from dataclasses import dataclass

# noinspection PyPep8Naming
@dataclass
class regex_in:
    string: str

    def __eq__(self, other: str | re.Pattern):
        if isinstance(other, str):
            other = re.compile(other)
        assert isinstance(other, re.Pattern)
        # TODO extend for search and match variants
        return other.fullmatch(self.string) is not None

Now you can do something like:

match regex_in(validated_string):
    case r'\d+':
        print('Digits')
    case r'\s+':
        print('Whitespaces')
    case _:
        print('Something else')

Caveat #1 is that you can't pass re.compile'd patterns to the case directly, because then Python wants to match based on class. You have to save the pattern somewhere first.

Caveat #2 is that you can't actually use local variables either, because Python then interprets it as a name for capturing the match subject. You need to use a dotted name, e.g. putting the pattern into a class or enum:

class MyPatterns:
    DIGITS = re.compile('\d+')

match regex_in(validated_string):
    case MyPatterns.DIGITS:
        print('This works, it\'s all digits')

Groups

This could be extended even further to provide an easy way to access the re.Match object and the groups.

# noinspection PyPep8Naming
@dataclass
class regex_in:
    string: str
    match: re.Match = None

    def __eq__(self, other: str | re.Pattern):
        if isinstance(other, str):
            other = re.compile(other)
        assert isinstance(other, re.Pattern)
        # TODO extend for search and match variants
        self.match = other.fullmatch(self.string)
        return self.match is not None

    def __getitem__(self, group):
        return self.match[group]

# Note the `as m` in in the case specification
match regex_in(validated_string):
    case r'\d(\d)' as m:
        print(f'The second digit is {m[1]}')
        print(f'The whole match is {m.match}')
2 of 7
23

Clean solution

There is a clean solution to this problem. Just hoist the regexes out of the case-clauses where they aren't supported and into the match-clause which supports any Python object.

The combined regex will also give you better efficiency than could be had by having a series of separate regex tests. Also, the regex can be precompiled for maximum efficiency during the match process.

Example

Here's a worked out example for a simple tokenizer:

pattern = re.compile(r'(\d+\.\d+)|(\d+)|(\w+)|(".*)"')
Token = namedtuple('Token', ('kind', 'value', 'position'))
env = {'x': 'hello', 'y': 10}

for s in ['123', '123.45', 'x', 'y', '"goodbye"']:
    mo = pattern.fullmatch(s)
    match mo.lastindex:
        case 1:
            tok = Token('NUM', float(s), mo.span())
        case 2:
            tok = Token('NUM', int(s), mo.span())
        case 3:
            tok = Token('VAR', env[s], mo.span())
        case 4:
            tok = Token('TEXT', s[1:-1], mo.span())
        case _:
            raise ValueError(f'Unknown pattern for {s!r}')
    print(tok) 

This outputs:

Token(kind='NUM', value=123, position=(0, 3))
Token(kind='NUM', value=123.45, position=(0, 6))
Token(kind='VAR', value='hello', position=(0, 1))
Token(kind='VAR', value=10, position=(0, 1))
Token(kind='TEXT', value='goodbye', position=(0, 9))

Better Example

The code can be improved by writing the combined regex in verbose format for intelligibility and ease of adding more cases. It can be further improved by naming the regex sub patterns:

pattern = re.compile(r"""(?x)
    (?P<float>\d+\.\d+) |
    (?P<int>\d+) |
    (?P<variable>\w+) |
    (?P<string>".*")
""")

That can be used in a match/case statement like this:

for s in ['123', '123.45', 'x', 'y', '"goodbye"']:
    mo = pattern.fullmatch(s)
    match mo.lastgroup:
        case 'float':
            tok = Token('NUM', float(s), mo.span())
        case 'int':
            tok = Token('NUM', int(s), mo.span())
        case 'variable':
            tok = Token('VAR', env[s], mo.span())
        case 'string':
            tok = Token('TEXT', s[1:-1], mo.span())
        case _:
            raise ValueError(f'Unknown pattern for {s!r}')
    print(tok)

Comparison to if/elif/else

Here is the equivalent code written using an if-elif-else chain:

for s in ['123', '123.45', 'x', 'y', '"goodbye"']:
    if (mo := re.fullmatch('\d+\.\d+', s)):
        tok = Token('NUM', float(s), mo.span())
    elif (mo := re.fullmatch('\d+', s)):
        tok = Token('NUM', int(s), mo.span())
    elif (mo := re.fullmatch('\w+', s)):
        tok = Token('VAR', env[s], mo.span())
    elif (mo := re.fullmatch('".*"', s)):
        tok = Token('TEXT', s[1:-1], mo.span())
    else:
        raise ValueError(f'Unknown pattern for {s!r}')
    print(tok)

Compared to the match/case, the if-elif-else chain is slower because it runs multiple regex matches and because there is no precompilation. Also, it is less maintainable without the case names.

Because all the regexes are separate we have to capture all the match objects separately with repeated use of assignment expressions with the walrus operator. This is awkward compared to the match/case example where we only make a single assignment.