To expand on what I said in comments: match introduces a value, but case introduces a pattern to match against. It is not an expression that is evaluated. In case the pattern represents a class, the stuff in the parentheses is not passed to a constructor, but is matched against attributes of the match value. Here is an illustrative example:

class Tree:
    def __init__(self, name):
        self.kind = name
    
def what_is(t):
    match t:
        case Tree(kind="oak"):
            return "oak"
        case Tree():
            return "tree"
        case _:
            return "shrug"

print(what_is(Tree(name="oak")))    # oak
print(what_is(Tree(name="birch")))  # tree
print(what_is(17))                  # shrug

Note here that outside case, Tree(kind="oak") would be an error:

TypeError: Tree.__init__() got an unexpected keyword argument 'kind'

And, conversely, case Tree(name="oak") would never match, since Tree instances in my example would not normally have an attribute named name.

This proves that case does not invoke the constructor, even if it looks like an instantiation.


EDIT: About your second error: you wrote case type(Tree()):, and got TypeError: type() accepts 0 positional sub-patterns (1 given). What happened here is this: case type(...) is, again, specifying a pattern. It is not evaluated as an expression. It says the match value needs to be of type type (i.e. be a class), and it has to have attributes in the parentheses. For example,

match Tree:
    case type(__init__=initializer):
        print("Tree is a type, and this is its initializer:")
        print(initializer)

This would match, and print something like

# => Tree is a type, and this is its initializer:
#    <function Tree.__init__ at 0x1098593a0>

The same case would not match an object of type Tree, only the type itself!

However, you can only use keyword arguments in this example. Positional arguments are only available if you define __match_args__, like the documentation says. The type type does not define __match_args__, and since it is an immutable built-in type, case type(subpattern, ...) (subpattern! not subexpression!), unlike case type(attribute=subpattern, ...), will always produce an error.

Answer from Amadan on Stack Overflow
🌐
Python
peps.python.org › pep-0636
PEP 636 – Structural Pattern Matching: Tutorial | peps.python.org
The match statement will check patterns from top to bottom. If the pattern doesn’t match the subject, the next pattern will be tried. However, once the first matching pattern is found, the body of that case is executed, and all further cases are ignored.
🌐
Plain English Westminster
benhoyt.com › writings › python-pattern-matching
Structural pattern matching in Python 3.10
Python evaluates the match expression, and then tries each case from the top, executing the first one that matches, or the _ default case if no others match. But here’s where the structural part comes in: the case patterns don’t just have to be literals.
Discussions

How to use Python pattern matching to match class types? - Stack Overflow
How can we use Python's structural pattern matching (introduced in 3.10) to match the type of a variable without invoking the constructor / in a case where a new instantiation of the class is not e... More on stackoverflow.com
🌐 stackoverflow.com
8 Levels of Using Structure Pattern Matching in Python
Nice article! It would help to have side-by-side comparisons of how you would accomplish the same tasks with if/else statements instead, to better illustrate the clarity and efficiency of the match statement. For example, this one's not so different: def handle_http_status(status_code): if status_code == 200: return "Success!" elif status_code in (400, 401, 403, 404): return f"Client Error:{status_code}" elif status_code in (500, 501, 502, 503, 504): return f"Server Error:{status_code}" else: return "Unknown Status" The "as err" option doesn't do much in the examples given, since you already have that value in status_code. So I simply reference status_code in the return lines. The shape example could be expressed with if/else like so: def process_shape(shape): if isinstance(shape, Circle): return f"Processing a circle with radius {shape.radius}" elif isinstance(shape, Rectangle): return f"Processing a rectangle with length {shape.length} and width {shape.width}" if isinstance(shape, Triangle): return f"Processing a triangle with base {shape.base} and height {shape.height}" else: return "Unknown shape" This is certainly more verbose than the match statement. Again, I have simply shifted the variable capture into the return statement, because why not? So far, so good. But wait! Let's try that dict example. def handle_user_action(action): if all(k in action for k in ('type', 'username', 'password')) and action['type']=='login': return f"Login attempt by {action['username']} with password {action['password']}" elif all(k in action for k in ('type', 'username')) and action['type']=='logout': return f"User {action['username']} logged out" elif all(k in action for k in ('type', 'username', 'email')) and action['type']=='signup': return f"New signup by {action['username']} with email {action['email']}" else: return "Unknown action" YUCK! Painful to read, and painful to write. You end up using the literal keys multiple times, leaving more potential for typos and sneaky bugs. You need to check each key's existence separately from checking its value. What a mess. I kept this example as simple as I could while maintaining a perfectly equivalent logical flow to the match example in the article, and keeping each if condition independent from others. In reality, if I had to use if/elif/else for this, I would use a fundamentally different structure, because this one sucks. I would probably split this into multiple if blocks, and capture the values into variables as early as possible to avoid using literal keys multiple times. This would add additional complexity. For example, I could check for the existence of the 'type' and 'username' keys first, before even starting this if statement, since every single case we use checks for them. But then what happens if I need to add more complex cases later that don't have all those same requirements? Better to keep each case independent -- or at least, it's better with the match statement, because it's designed to make that easy, efficient, safe, and readable. It's difficult to express the utility of the match statement with simple examples, because if/else is perfectly fine for those (always has been!). The simple examples don't really justify the complexity of adding a whole new type of statement block, but for advanced use cases, the advantage of match over if/elif/else widens significantly. There are even more complex and powerful things you can do with match. See https://peps.python.org/pep-0636/ for more details. More on reddit.com
🌐 r/Python
10
113
December 24, 2023
How to do structural pattern matching in Python 3.10 with a type to match? - Stack Overflow
I am trying to match a type in Python 3.10 using the console: t = 12.0 match type(t): case int: print("int") case float: print("float") And I get this error: File... More on stackoverflow.com
🌐 stackoverflow.com
You can use 3.10's new structural pattern matching feature to easily flatten deeply nested lists, tuples and sets.
I programmatically solved this in python 3.8, I'm excited to see how 3.10 simplifies this. Thanks for the post More on reddit.com
🌐 r/Python
60
534
November 22, 2021
🌐
Real Python
realpython.com › structural-pattern-matching
Structural Pattern Matching in Python – Real Python
August 6, 2024 - The match statement takes a subject, which can be any valid Python expression, such as a string literal or a function call, and compares the resulting value to one or more patterns listed in the case clauses. The first pattern that matches the given subject will trigger the corresponding case block to run.
Top answer
1 of 1
7

To expand on what I said in comments: match introduces a value, but case introduces a pattern to match against. It is not an expression that is evaluated. In case the pattern represents a class, the stuff in the parentheses is not passed to a constructor, but is matched against attributes of the match value. Here is an illustrative example:

class Tree:
    def __init__(self, name):
        self.kind = name
    
def what_is(t):
    match t:
        case Tree(kind="oak"):
            return "oak"
        case Tree():
            return "tree"
        case _:
            return "shrug"

print(what_is(Tree(name="oak")))    # oak
print(what_is(Tree(name="birch")))  # tree
print(what_is(17))                  # shrug

Note here that outside case, Tree(kind="oak") would be an error:

TypeError: Tree.__init__() got an unexpected keyword argument 'kind'

And, conversely, case Tree(name="oak") would never match, since Tree instances in my example would not normally have an attribute named name.

This proves that case does not invoke the constructor, even if it looks like an instantiation.


EDIT: About your second error: you wrote case type(Tree()):, and got TypeError: type() accepts 0 positional sub-patterns (1 given). What happened here is this: case type(...) is, again, specifying a pattern. It is not evaluated as an expression. It says the match value needs to be of type type (i.e. be a class), and it has to have attributes in the parentheses. For example,

match Tree:
    case type(__init__=initializer):
        print("Tree is a type, and this is its initializer:")
        print(initializer)

This would match, and print something like

# => Tree is a type, and this is its initializer:
#    <function Tree.__init__ at 0x1098593a0>

The same case would not match an object of type Tree, only the type itself!

However, you can only use keyword arguments in this example. Positional arguments are only available if you define __match_args__, like the documentation says. The type type does not define __match_args__, and since it is an immutable built-in type, case type(subpattern, ...) (subpattern! not subexpression!), unlike case type(attribute=subpattern, ...), will always produce an error.

🌐
Reddit
reddit.com › r/python › 8 levels of using structure pattern matching in python
r/Python on Reddit: 8 Levels of Using Structure Pattern Matching in Python
December 24, 2023 -

There is one feature that Python developers waiting for so long: structural pattern matching. It finally became possible since Python 3.10. This article will show you all tricks of it in 8 levels of difficulty.

Top answer
1 of 4
31
Nice article! It would help to have side-by-side comparisons of how you would accomplish the same tasks with if/else statements instead, to better illustrate the clarity and efficiency of the match statement. For example, this one's not so different: def handle_http_status(status_code): if status_code == 200: return "Success!" elif status_code in (400, 401, 403, 404): return f"Client Error:{status_code}" elif status_code in (500, 501, 502, 503, 504): return f"Server Error:{status_code}" else: return "Unknown Status" The "as err" option doesn't do much in the examples given, since you already have that value in status_code. So I simply reference status_code in the return lines. The shape example could be expressed with if/else like so: def process_shape(shape): if isinstance(shape, Circle): return f"Processing a circle with radius {shape.radius}" elif isinstance(shape, Rectangle): return f"Processing a rectangle with length {shape.length} and width {shape.width}" if isinstance(shape, Triangle): return f"Processing a triangle with base {shape.base} and height {shape.height}" else: return "Unknown shape" This is certainly more verbose than the match statement. Again, I have simply shifted the variable capture into the return statement, because why not? So far, so good. But wait! Let's try that dict example. def handle_user_action(action): if all(k in action for k in ('type', 'username', 'password')) and action['type']=='login': return f"Login attempt by {action['username']} with password {action['password']}" elif all(k in action for k in ('type', 'username')) and action['type']=='logout': return f"User {action['username']} logged out" elif all(k in action for k in ('type', 'username', 'email')) and action['type']=='signup': return f"New signup by {action['username']} with email {action['email']}" else: return "Unknown action" YUCK! Painful to read, and painful to write. You end up using the literal keys multiple times, leaving more potential for typos and sneaky bugs. You need to check each key's existence separately from checking its value. What a mess. I kept this example as simple as I could while maintaining a perfectly equivalent logical flow to the match example in the article, and keeping each if condition independent from others. In reality, if I had to use if/elif/else for this, I would use a fundamentally different structure, because this one sucks. I would probably split this into multiple if blocks, and capture the values into variables as early as possible to avoid using literal keys multiple times. This would add additional complexity. For example, I could check for the existence of the 'type' and 'username' keys first, before even starting this if statement, since every single case we use checks for them. But then what happens if I need to add more complex cases later that don't have all those same requirements? Better to keep each case independent -- or at least, it's better with the match statement, because it's designed to make that easy, efficient, safe, and readable. It's difficult to express the utility of the match statement with simple examples, because if/else is perfectly fine for those (always has been!). The simple examples don't really justify the complexity of adding a whole new type of statement block, but for advanced use cases, the advantage of match over if/elif/else widens significantly. There are even more complex and powerful things you can do with match. See https://peps.python.org/pep-0636/ for more details.
2 of 4
7
Nice summary. Especially the last couple entries-- matching against a dictionary and matching against a class declaratio, I would never have thought to try. And they’re really useful!
🌐
GeeksforGeeks
geeksforgeeks.org › python › pattern-matching-python-regex
Pattern matching in Python with Regex - GeeksforGeeks
January 19, 2026 - (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 the occurrence of the pattern which we are looking for. ... # Python program to illustrate # Matching Specific Repetitions # with Curly Brackets import re haRegex = re.compile(r'(Ha){3}') mo1 = haRegex.search('HaHaHa') print(mo1.group())
Find elsewhere
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.

🌐
Python documentation
docs.python.org › 3 › library › re.html
re — Regular expression operations
4 days ago - This means that r'\bat\b' matches 'at', 'at.', '(at)', and 'as at ay' but not 'attempt' or 'atlas'. The default word characters in Unicode (str) patterns are Unicode alphanumerics and the underscore, but this can be changed by using the ASCII flag. Word boundaries are determined by the current locale if the LOCALE flag is used. ... Inside a character range, \b represents the backspace character, for compatibility with Python’s string literals.
🌐
Python
peps.python.org › pep-0635
PEP 635 – Structural Pattern Matching: Motivation and Rationale | peps.python.org
Many Python applications and libraries are not written in a consistent OO style – unlike Java, Python encourages defining functions at the top-level of a module, and for simple data structures, tuples (or named tuples or lists) and dictionaries are often used exclusively or mixed with classes or data classes. Pattern matching is particularly suitable for picking apart such data structures.
🌐
Medium
medium.com › data-engineering-with-dremio › leveraging-pythons-pattern-matching-and-comprehensions-for-data-analytics-9293a7921db5
Leveraging Python’s Pattern Matching and Comprehensions for Data Analytics | by Alex Merced | Data, Analytics & AI with Dremio | Medium
November 7, 2024 - Pattern matching, introduced in Python 3.10, allows for more intuitive and readable conditional logic by enabling the matching of complex data structures with minimal code. This feature is particularly useful in data analytics when dealing with ...
🌐
ArjanCodes
arjancodes.com › blog › how-to-use-structural-pattern-matching-in-python
Introduction to Structural Pattern Matching in Python | ArjanCodes
June 20, 2024 - Structural pattern matching is a feature that allows you to check a value against a pattern, extracting parts of the value if the pattern matches. This can make complex conditionals simpler and more readable.
🌐
Python.org
discuss.python.org › ideas
Partial string matches in structural pattern matching - Ideas - Discussions on Python.org
July 20, 2023 - Hi there, I’d love to be able to use partial string matches: match text: case "prefix_" + cmd: # checking prefix print("got", cmd) case "Hello " + last_name + ", " + first_name + "!": # more complex e…
🌐
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...
🌐
Python
peps.python.org › pep-0622
PEP 622 – Structural Pattern Matching | peps.python.org
The pattern syntax builds on Python’s existing syntax for sequence unpacking (e.g., a, b = value). A match statement compares a value (the subject) to several different shapes (the patterns) until a shape fits.
🌐
Martin Heinz
martinheinz.dev › blog › 78
Recipes and Tricks for Effective Structural Pattern Matching in Python | Martin Heinz | Personal Website & Blog
August 2, 2022 - Common use case for match/case is efficient matching of JSON structures in form of Python's dictionaries. This can be done with mapping pattern which is triggered by case {...}: ...
🌐
Inspired Python
inspiredpython.com › course › pattern-matching › mastering-structural-pattern-matching
Mastering Structural Pattern Matching • Inspired Python
When you write a pattern you describe the structure the case clause should test the subject against. That opens up a lot of avenues that are otherwise unavailable to you. You can deeply nest dictionaries, lists and tuples and Python’s matching engine will delicately unwrap each layer and check if the structure matches any of the case clauses.
🌐
Python Engineer
python-engineer.com › posts › pattern-matching-python
Master Pattern Matching In Python 3.10 | All Options | - Python Engineer
You use the match keyword and the case keyword and can then match a variable against different values. For each matched case you can then perform a certain action. In the simplest case like here we match against simple values, but you can also ...
🌐
Python
peps.python.org › pep-0634
PEP 634 – Structural Pattern Matching: Specification | peps.python.org
An OR pattern matches each of its subpatterns in turn to the subject, until one succeeds. The OR pattern is then deemed to succeed. If none of the subpatterns succeed the OR pattern fails.
🌐
W3Schools
w3schools.com › python › python_match.asp
Python Match
month = 5 day = 4 match day: case 1 | 2 | 3 | 4 | 5 if month == 4: print("A weekday in April") case 1 | 2 | 3 | 4 | 5 if month == 5: print("A weekday in May") case _: print("No match") Try it Yourself » ... If you want to use W3Schools services as an educational institution, team or enterprise, send us an e-mail: sales@w3schools.com · If you want to report an error, or if you want to make a suggestion, send us an e-mail: help@w3schools.com · HTML Tutorial CSS Tutorial JavaScript Tutorial How To Tutorial SQL Tutorial Python Tutorial W3.CSS Tutorial Bootstrap Tutorial PHP Tutorial Java Tutorial C++ Tutorial jQuery Tutorial