🌐
Python
peps.python.org › pep-0622
PEP 622 – Structural Pattern Matching | peps.python.org
Like unpacking assignment, both tuple-like and list-like syntax can be used, with identical semantics. Each element can be an arbitrary pattern; there may also be at most one *name pattern to catch all remaining items: match collection: case 1, [x, *others]: print("Got 1 and a nested sequence") case (1, x): print(f"Got 1 and {x}")
🌐
Python
peps.python.org › pep-0636
PEP 636 – Structural Pattern Matching: Tutorial | peps.python.org
The fourth pattern captures two values, which makes it conceptually similar to the unpacking assignment (x, y) = point. If you are using classes to structure your data you can use the class name followed by an argument list resembling a constructor, but with the ability to capture attributes into variables: from dataclasses import dataclass @dataclass class Point: x: int y: int def where_is(point): match point: case Point(x=0, y=0): print("Origin") case Point(x=0, y=y): print(f"Y={y}") case Point(x=x, y=0): print(f"X={x}") case Point(): print("Somewhere else") case _: print("Not a point")
🌐
Plain English Westminster
benhoyt.com › writings › python-pattern-matching
Structural pattern matching in Python 3.10
At a recent local Python meetup, a friend was presenting some of the new features in Python 3.8 and 3.9, and afterwards we got to talking about the pattern matching feature coming in Python 3.10. I went on a mild rant about how I thought Python had lost the plot: first assignment expressions using :=, and now this rather sprawling feature.
🌐
Real Python
realpython.com › structural-pattern-matching
Structural Pattern Matching in Python – Real Python
August 6, 2024 - The patterns that you’ll examine in this section will let you match and destructure popular collection types, as well as instances of classes. More specifically, you’ll learn about the sequence and mapping patterns. You’ll also learn that Python doesn’t support their set counterparts. You’ll take a look at the sequence pattern first, which closely resembles iterable unpacking. With this kind of structural pattern, you can assign values from a list or tuple to multiple variables in a single line of code while controlling your execution flow.
🌐
Mathspp
mathspp.com › blog › pydonts › structural-pattern-matching-tutorial
Structural pattern matching tutorial | Pydon't 🐍 | mathspp
I covered these in detail in “Unpacking with starred assignments” and “Deep unpacking”, so go read those Pydon'ts if you are unfamiliar with how to use these features to write Pythonic code. The match statement will use ideas from both starred assignments and deep unpacking, so knowing how to use them is going to be helpful.
🌐
Python
peps.python.org › pep-0635
PEP 635 – Structural Pattern Matching: Motivation and Rationale | peps.python.org
We thus consistently use the term ... in patterns. The match statement evaluates an expression to produce a subject, finds the first pattern that matches the subject, and executes the associated block of code....
🌐
InfoWorld
infoworld.com › home › software development › programming languages › python
How to use structural pattern matching in Python | InfoWorld
August 9, 2023 - In fact, the chief use case for structural pattern matching is to match patterns of types, rather than patterns of values. Python performs matches by going through the list of cases from top to bottom. On the first match, Python executes the statements in the corresponding case block, then skips to the end of the match block and continues with the rest of the program.
Top answer
1 of 10
68

So far as I know there's no way to make it a one-liner in current Python without introducing another function, e.g.:

split_list = lambda lst: (lst[0], lst[1:])
head, rest = split_list(my_func())

However, in Python 3.0 the specialized syntax used for variadic argument signatures and argument unpacking will become available for this type of general sequence unpacking as well, so in 3.0 you'll be able to write:

head, *rest = my_func()

See PEP 3132 for details.

2 of 10
34

First of all, please note that the "pattern matching" of functional languages and the assignment to tuples you mention are not really that similar. In functional languages the patterns are used to give partial definitions of a function. So f (x : s) = e does not mean take the head and tail of the argument of f and return e using them, but it means that if the argument of f is of the form x : s (for some x and s), then f (x : s) is equal to e.

The assignment of python is more like a multiple assignment (I suspect that was its original intention). So you write, for example, x, y = y, x to swap the values in x and y without needing a temporary variable (as you would with a simple assignment statement). This has little to do with pattern matching as it is basically a shorthand for the "simultaneous" execution of x = y and y = x. Although python allows arbitrary sequences instead of comma-separated lists, I would not suggest calling this pattern matching. With pattern matching you check whether or not something matches a pattern; in the python assignment you should ensure that the sequences on both sides are the same.

To do what you seem to want you would usually (also in functional languages) use either a auxiliary function (as mentioned by others) or something similar to let or where constructs (which you can regard as using anonymous functions). For example:

(head, tail) = (x[0], x[1:]) where x = my_func()

Or, in actual python:

(head, tail) = (lambda x: (x[0], x[1:]))(my_func())

Note that this is essentially the same as the solutions given by others with an auxiliary function except that this is the one-liner you wanted. It is, however, not necessarily better than a separate function.

(Sorry if my answer is a bit over the top. I just think it's important to make the distinction clear.)

🌐
Inspired Python
inspiredpython.com › course › pattern-matching › mastering-structural-pattern-matching
Mastering Structural Pattern Matching • Inspired Python
match <expression>: case <pattern ... The match statement is a soft keyword, and takes an expression (think: similar to the right-hand side of a variable assignment) and that becomes the subject of your case clauses...
Find elsewhere
🌐
Python.org
discuss.python.org › ideas
Feature Suggestion - Assign match case result to a variable - Ideas - Discussions on Python.org
August 7, 2023 - Feature or enhancement Assign match-case result to a variable Pitch Currently, in Python 3.10+, match-case statements allow us to perform pattern matching and execute code blocks based on patterns.
🌐
Python Course
python-course.eu › python-tutorial › structural-pattern-matching.php
17. Structural Pattern Matching | Python Tutorial | python-course.eu
With structural pattern matching, a data structure (such as a list or a tuple) is analyzed and searched for a specific pattern that one has previously defined. One can then perform different actions based on the patterns found.
🌐
Coditation
coditation.com › blog › structural-pattern-matching-in-python-i
Structural Pattern Matching in Python I
Similarly, 0 will be a match for the subject value False. F-strings are not allowed to be used as patterns or cases. These are patterns where we can assign a subject(partially or fully) to a free variable.
🌐
Python Engineer
python-engineer.com › posts › pattern-matching-python
Master Pattern Matching In Python 3.10 | All Options | - Python Engineer
def http_error(status): match status: case 400: return "Bad request" case _: return "Something's wrong with the internet" Patterns can look like unpacking assignments, and a pattern may be used to bind variables.
🌐
Earthly
earthly.dev › blog › structural-pattern-matching-python
Structural Pattern Matching in Python - Earthly Blog
July 19, 2023 - The match-case statement differs from the switch-case statement in that it does not require an explicit break statement after a pattern has been matched. It also has a lot of powerful features that cannot be found in the switch-case statement in other languages. We will explore these features later on. The patterns are shapes or structures that the subjects are compared against. The values of the subject can also be captured and bound to a variable that we specified in the pattern. Binding variables to values is a little different than assigning variables to values.
🌐
GeeksforGeeks
geeksforgeeks.org › pattern-matching-python-regex
Pattern matching in Python with Regex - GeeksforGeeks
July 6, 2024 - The (wo)? part of the regular expression means that the pattern wo is an optional group. The regex will match text that has zero instances or one instance of wo in it. This is why the regex matches both 'Batwoman' and 'Batman'. You can think of the ? as saying,groups “Match zero or one of the group preceding this question mark.” · If you need to match an actual question mark character, escape it with \?. ... # Python program to illustrate # optional matching # with question mark(?) import re batRegex = re.compile(r'Bat(wo)?man') mo1 = batRegex.search('The Adventures of Batman') mo2 = batRegex.search('The Adventures of Batwoman') print(mo1.group()) print(mo2.group())
Top answer
1 of 4
22

This is a common "gotcha" of the new syntax: case clauses are not expressions. That is, if you put a variable name in a case clause, the syntax assigns to that name rather than reading that name.

It's a common misconception to think of match as like switch in other languages: it is not, not even really close. switch cases are expressions which test for equality against the switch expression; conversely, match cases are structured patterns which unpack the match expression. It's really much more akin to generalized iterable unpacking. It asks the question: "does the structure of the match expression look like the structure of the case clause?", a very different question from what a switch statement asks.

For example:

t = 12.0
match t:
    case newvar: # This is equal to `newvar = t`
        print(f"bound a new variable called newvar: {newvar}")
        # prints "bound a new variable called newvar: 12.00000000"
        # this pattern matches anything at all, so all following cases never run

    case 13.0:
        print("found 13.0")

    case [a, b, c]: # matches an iterable with exactly 3 elements,
        # and *assigns* those elements to the variables `a`, `b` and `c`
        print(f"found an iterable of length exactly 3.")
        print(f"these are the values in the iterable: {a} {b} {c}")

    case [*_]:
        print("found some sort of iterable, but it's definitely")
        print("not of length 3, because that already matched earlier")

    case my_fancy_type(): # match statement magic: this is how to type check!
        print(f"variable t = {t} is of type {my_fancy_type}")

    case _:
        print("no match")

So what your OP actually does is kinda like this:

t = 12.0
tt = type(t) # float obviously
match tt:

    case int: # assigns to int! `int = tt`, overwriting the builtin
       print(f"the value of int: {int}")
       # output: "the value of int: <class 'float'>"
       print(int == float) # output: True (!!!!!!!!)
       # In order to get the original builtin type, you'd have to do
       # something like `from builtins import int as int2`

    case float: # assigns to float, in this case the no-op `float = float`
        # in fact this clause is identical to the previous clause:
        # match anything and bind the match to its new name
        print(f"match anything and bind it to name 'float': {float}")
        # never prints, because we already matched the first case

    case float(): # since this isn't a variable name, no assignment happens.
        # under the hood, this equates to an `isinstance` check. 
        # `float` is not an instance of itself, so this wouldn't match.
        print(f"tt: {tt} is an instance of float") # never prints
        # of course, this case never executes anyways because the
        # first case matches anything, skipping all following cases

Frankly, I'm not entirely sure how the under-the-hood instance check works, but it definitely works like the other answer says: by defintion of the match syntax, type checks are done like this:

match instance:
    case type():
        print(f"object {instance} is of type {type}!")

So we come back to where we started: case clauses are not expressions. As the PEP says, it's better to think of case clauses as kind of like function declarations, where we name the arguments to the function and possibly bind some default values to those newly-named arguments. But we never, ever read existing variables in case clauses, only make new variables. (There's some other subtleties involved as well, for instance a dotted access doesn't count as a "variable" for this purpose, but this is complicated already, best to end this answer here.)

2 of 4
17

Lose the type() and also add parentheses to your types:

t = 12.0
match t:
  case int():
    print("int")
  case float():
    print("float")

I'm not sure why what you've wrote is not working, but this one works.

🌐
LWN.net
lwn.net › Articles › 827179
"Structural pattern matching" for Python, part 1 [LWN.net]
Posted Aug 9, 2020 15:39 UTC (Sun) by kleptog (subscriber, #1183) [Link] Elixir uses pattern matching extensively and they simply prefix it with a tilde, like so: iex> x = 1 1 iex> case 10 do ...> ^x -> "Won't match" ...> _ -> "Will match" ...> end "Will match" Seems like a straightforward approach. Posted Aug 5, 2020 19:10 UTC (Wed) by ibukanov (subscriber, #3942) [Link] (3 responses) Python has limited support for destructing assignments so one can write: x, _, y = (1, 2, 3).
🌐
Martin Heinz
martinheinz.dev › blog › 78
Recipes and Tricks for Effective Structural Pattern Matching in Python | Martin Heinz | Personal Website & Blog
August 2, 2022 - You might try to "simplify" this ... it would assign the value of food into fruit and vegetable respectively. To avoid this X.some_var must be used because dots always trigger the value pattern. It's pretty common to write a conditionals in Python that test what the variable type is. You can use structural pattern matching for this, ...
Top answer
1 of 5
87

If the constant you're testing against is a dotted name, then it should be treated as a constant instead of as the name of the variable to put the capture in (see PEP 636 # Matching against constants and enums):

Copyclass Codes:
    SUCCESS = 200
    NOT_FOUND = 404

def handle(retcode):
    match retcode:
        case Codes.SUCCESS:
            print('success')
        case Codes.NOT_FOUND:
            print('not found')
        case _:
            print('unknown')

Although, given how python is trying to implement pattern-matching, I think that for situations like this it's probably safer and clearer code to just use an if/elif/else tower when checking against constant values.

2 of 5
59

Hopefully I can help shed some light on why bare names work this way here.

First, as others have already noted, if you need to match values as part of your patterns, you can do so by:

  • Matching supported literals, like numbers, strings, booleans, and None
  • Matching qualified (dotted) names
  • Using additional tests in guards (which are separated from patterns by if)

I fear that we (the PEP authors) probably made a small error by including this toy snippet in an early tutorial... it's since gone a bit viral. Our goal was to lead with the simplest possible example of pattern matching, but we instead seem to have also created a confusing first impression for many (especially when repeated without context).

The most overlooked word in the title of these PEPs is "structural". If you're not matching the structure of the subject, structural pattern matching probably isn't the right tool for the job.

The design of this feature was driven by destructuring (like iterable unpacking on the LHS of assignments, but generalized for all objects), which is why we made it very easy to perform the core functionality of extracting out parts of an object and binding them to names. We also decided that it would also be useful to allow programmers to match on values, so we added those (with the condition that when the values are named, they must be qualified with a dot, in order to distinguish them from the more common extractions).

Python's pattern matching was never really designed with the intent of powering C-style switch statements like this; that's been proposed for Python (and rejected) twice before, so we chose to go in a different direction. Besides, there is already one obvious way to switch on a single value, which is simpler, shorter, and works on every version of Python: a good-ol' if/elif/else ladder!

CopySUCCESS = 200
NOT_FOUND = 404

def handle(retcode):
    if retcode == SUCCESS:
        print('success')
    elif retcode == NOT_FOUND:
        print('not found')
    else:
        print('unknown')

handle(404)

(If you're really concerned about performance or need an expression, dispatching from a dictionary is also a fine alternative.)

🌐
Tobiaskohn
tobiaskohn.ch › index.php › 2018 › 09 › 12 › implementing-pattern-matching
Implementing Pattern Matching in Python – Tobias Kohn
If node is found to correspond to the specified structure, the inner operand is extracted, and assigned to the local variable x. This is a crucial part of pattern matching: without understanding the double nature of test and assignment, nothing wonderful can come of the story I am about to ...