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 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
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. The patterns can also: ... Wow! That’s a lot of features. Let’s see if we can use them all in one go, to see what they look like in a very contrived example (for a more gradual introduction, read the tutorial): class Car: __match_args__ = ('key', 'name') def __init__(self, key, name): self.key = key self.name = name expr = eval(input('
🌐
Python
peps.python.org › pep-0622
PEP 622 – Structural Pattern Matching | peps.python.org
For example, collections.defaultdict instances will only match patterns with keys that were already present when the match block was entered. ... class_pattern: | name_or_attr '(' ')' | name_or_attr '(' ','.pattern+ ','? ')' | name_or_attr '(' ','.keyword_pattern+ ','? ')' | name_or_attr '(' ','.pattern+ ',' ','.keyword_pattern+ ','? ')' keyword_pattern: NAME '=' or_pattern
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.

🌐
Real Python
realpython.com › structural-pattern-matching
Structural Pattern Matching in Python – Real Python
August 6, 2024 - To facilitate the use of pattern matching in Python, the interpreter itself had to undergo significant changes. Python 3.9 introduced the concept of soft keywords, and Python 3.10 delivered their first implementation. Standard keywords like with or return are reserved, meaning that you can’t use them as identifiers for your variables, functions, and other elements in your code. As soon as you try, you’ll encounter a syntax error: ... >>> ticket.class = "business" File "<input>", line 1 ticket.class = "business" ^^^^^ SyntaxError: invalid syntax
🌐
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.
🌐
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. ... def http_status(status: int) -> str: match status: case 200: return "OK" case 404: return "Not Found" case 500: return "Internal Server Error" case _: return "Unknown Error" In the example above, match is a keyword introduced in Python 3.10, which, combined with case, allows for clean and readable pattern matching.
Find elsewhere
🌐
Python
peps.python.org › pep-0634
PEP 634 – Structural Pattern Matching: Specification | peps.python.org
For example, collections.defaultdict instances will only be matched by patterns with keys that were already present when the match statement was entered. ... class_pattern: | name_or_attr '(' [pattern_arguments ','?] ')' pattern_arguments: | positional_patterns [',' keyword_patterns] | keyword_patterns positional_patterns: ','.pattern+ keyword_patterns: ','.keyword_pattern+ keyword_pattern: NAME '=' pattern
🌐
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. (If it had tried that, we would have seen another “Connecting to server” message.) So even if you have side effects in your __init__ method there are some safeguards made to avoid causing them directly. Having said that, where possible you should move that sort of logic into a dedicated class method that does that work for you.
🌐
Mathspp
mathspp.com › blog › pydonts › structural-pattern-matching-tutorial
Structural pattern matching tutorial | Pydon't 🐍 | mathspp
Structural pattern matching can also be used to match the structure of class instances. Let us recover the Point2D class I have used as an example in a couple of posts, in particular the Pydon't about __str__ and __repr__:
🌐
Coditation
coditation.com › blog › structural-pattern-matching-in-python-iii
Structural Pattern Matching in Python III
# Matching a class pattern with ... attribute class Furniture: __match_args__ = ("price", "name") def __init__(self, name: str, price: float): self.name = name self.price = price subject = Furniture("table", 2200) # Output: The ...
🌐
Earthly
earthly.dev › blog › structural-pattern-matching-python
Structural Pattern Matching in Python - Earthly Blog
July 19, 2023 - If positional patterns are present in a class, they are converted to keyword patterns based on the arrangement in the __match_args__ attribute. In this tutorial, we delved into Python 3.10’s structural pattern matching feature. You learned about various patterns, like literal, capture, wildcard, AS, OR, sequence, mapping, and class.
🌐
Martin Heinz
martinheinz.dev › blog › 78
Recipes and Tricks for Effective Structural Pattern Matching in Python | Martin Heinz | Personal Website & Blog
August 2, 2022 - Hopefully, future versions of Python will include some level of code analysis that might catch at least some of these issues. Another hard to debug issue you might encounter stems from missing a valid branch/case - that is - when match doesn't cover all the possible cases. This can be mitigated by adding safety asserts like so: from enum import Enum from typing import NoReturn class Color(Enum): RED = "Red" GREEN = "Green" BLUE = "Blue" def exhaustiveness_check(value: NoReturn) -> NoReturn: assert False, 'This code should never be reached, got: {0}'.format(value) def some_func(color: Color) -> str: match color: case Color.RED: return "Color is red." case Color.GREEN: return "Color is green." exhaustiveness_check(color) some_func(Color.RED)
🌐
InfoWorld
infoworld.com › home › software development › programming languages › python
How to use structural pattern matching in Python | InfoWorld
August 9, 2023 - 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. There is no “fall-through” between cases, but it’s possible to design your logic to handle multiple possible cases in a single case block. (More on this later.) It’s also possible to capture all or part of a match and re-use it. In the case unknown_command in our example above, where no match has been made, the value is “captured” in the variable unknown_command so we can re-use it.
🌐
Medium
medium.com › @myexamcloud123 › pattern-matching-in-python-01ac3ea21598
Pattern Matching in Python. Pattern matching has become a powerful… | by MyExamCloud | Medium
October 5, 2024 - To match against the contents of variables, they need to be specified as dotted names, similar to enums. Here’s an example illustrating this concept: from enum import Enum class Command(Enum): DANCE = 0 SING = 1 match command: case Command.DANCE: ...
🌐
Buddy
buddy.works › home › tutorials › languages › structural pattern matching in python
Structural Pattern Matching In Python | Tutorials
April 28, 2021 - Any key that is not included in the pattern will be ignored while matching. With mapping patterns, we do not have to write code that validates the dictionary before extracting the values we want from the dict since we can specify the pattern we want. A common use case in Python is checking if one class is the subclass of another before performing an operation.
🌐
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:
🌐
Python.org
discuss.python.org › python help
Class type pattern matching - Python Help - Discussions on Python.org
1 week ago - I am comfused by this code and ... class A: class AA: pass class B(A): pass @dataclass class MyClass: foo: int bar: str def match_class_type(cls): match cls: case __builtins__.int: print(" is int") case __builtins__.str: print(" is str") # Why does ...
🌐
Coditation
coditation.com › blog › structural-pattern-matching-in-python-i
Structural Pattern Matching in Python I
An Irrefutable pattern in the above example value can only be used as the last pattern to be matched. The free variables a or b can be used outside the match scope while using an Irrefutable pattern value will result in UnboundLocalError: local variable 'value' referenced before assignment error · Unless a class pattern is explicitly mentioned the list and tuple can be matched Ex: subject case match (22,66) [a,b] matched [22,66] (a,b) matched (22,) [a,] matched [22,] (a,) matched