If you just pass a type directly, it will consider it to be a "name capture" rather than a "value capture." You can coerce it to use a value capture by importing the builtins module, and using a dotted notation to check for the type.

import builtins
from typing import Type


def main(type_: Type):
    match (type_):
        case builtins.str:  # it works with the dotted notation
            print(f"{type_} is a String")
        case builtins.int:
            print(f"{type_} is an Int")
        case _:
            print("\nhttps://en.meming.world/images/en/0/03/I%27ve_Never_Met_This_Man_In_My_Life.jpg")

if __name__ == "__main__":
    main(type("hello"))  # <class 'str'> is a String
    main(str)  # <class 'str'> is a String
    main(type(42))  # <class 'int'> is an Int
    main(int)  # <class 'int'> is an Int
Answer from Patryk Bratkowski on Stack Overflow
Top answer
1 of 4
22

If you just pass a type directly, it will consider it to be a "name capture" rather than a "value capture." You can coerce it to use a value capture by importing the builtins module, and using a dotted notation to check for the type.

import builtins
from typing import Type


def main(type_: Type):
    match (type_):
        case builtins.str:  # it works with the dotted notation
            print(f"{type_} is a String")
        case builtins.int:
            print(f"{type_} is an Int")
        case _:
            print("\nhttps://en.meming.world/images/en/0/03/I%27ve_Never_Met_This_Man_In_My_Life.jpg")

if __name__ == "__main__":
    main(type("hello"))  # <class 'str'> is a String
    main(str)  # <class 'str'> is a String
    main(type(42))  # <class 'int'> is an Int
    main(int)  # <class 'int'> is an Int
2 of 4
7

As its name suggests, structural pattern matching is more suited for matching patterns, not values (like a classic switch/case in other languages). For example, it makes it very easy to check different possible structures of a list or a dict, but for values there is not much advantage over a simple if/else structure:

if type_to_match == str:
    print("This is a String")
elif type_to_match == int:
    print("This is an Int")
else:
    print("\nhttps://en.meming.world/images/en/0/03/I%27ve_Never_Met_This_Man_In_My_Life.jpg")

But if you really want to use SPM, you could use the guard feature along with issublcass to check if a type is or is a child of any other:

match type_to_match:
    case s if issubclass(type_to_match, str):
        print(f"{s} - This is a String")
    case n if issubclass(type_to_match, int):
        print(f"{n} - This is an Int")
    case _:
        print("\nhttps://en.meming.world/images/en/0/03/I%27ve_Never_Met_This_Man_In_My_Life.jpg")
🌐
Python
peps.python.org › pep-0636
PEP 636 – Structural Pattern Matching: Tutorial | peps.python.org
A pattern like Click(position=(x, y)) only matches if the type of the event is a subclass of the Click class. It will also require that the event has a position attribute that matches the (x, y) pattern.
🌐
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
peps.python.org › pep-0622
PEP 622 – Structural Pattern Matching | peps.python.org
A match statement compares a value (the subject) to several different shapes (the patterns) until a shape fits. Each pattern describes the type and structure of the accepted values as well as the variables where to capture its contents.
🌐
Plain English Westminster
benhoyt.com › writings › python-pattern-matching
Structural pattern matching in Python 3.10
Originally I was also concerned that match’s class patterns don’t play well with Python’s use of duck typing, where you just access attributes and call methods on an object, without checking its type first (for example, when using file-like objects). With class patterns, however, you specify the type, and it performs an isinstance check.
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.

🌐
Coditation
coditation.com › blog › structural-pattern-matching-in-python-i
Structural Pattern Matching in Python I
Python 3.10 introduced structural pattern-matching syntax in python. Typically used in functional programming languages such as Haskell, rust and hybrid programming languages such as scala, more and more object-oriented programming languages are adopting spm syntax as a core feature. We start with basic definitions, types ...
🌐
Earthly
earthly.dev › blog › structural-pattern-matching-python
Structural Pattern Matching in Python - Earthly Blog
July 19, 2023 - The behavior of the match-case statement, in this case, is similar to the switch-case statement in Javascript. The match-case statement compares the value of the subject with the literal values specified as patterns in the case clauses.
Find elsewhere
🌐
Martin Heinz
martinheinz.dev › blog › 78
Recipes and Tricks for Effective Structural Pattern Matching in Python | Martin Heinz | Personal Website & Blog
August 2, 2022 - Instead, you have to use case float(), which triggers a class pattern and tests whether the variable in match ... is of the specified type. The above however only works for 9 builtin types, namely: bool, bytearray, bytes, dict, float, frozenset, int, list, set, str and tuple.
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.

🌐
Inspired Python
inspiredpython.com › course › pattern-matching › mastering-structural-pattern-matching
Mastering Structural Pattern Matching • Inspired Python
When I call the code it works as ... the matched value on the left-hand side to the name on the right. >>> as_pattern("Inspired Python") Here is your string='Inspired Python' >>> as_pattern(42) You said a number=42 · AS Patterns make it possible to bind grouped declarations · Without AS patterns you could only bind generic data in a pattern and never constrain it to a type, for ...
🌐
Buddy
buddy.works › home › tutorials › languages › structural pattern matching in python
Structural Pattern Matching In Python | Tutorials
April 28, 2021 - A common use case in Python is checking if one class is the subclass of another before performing an operation. Python's pattern matching can also match classes, allowing us to check an object's type. Any class can be matched, even the built-in ...
🌐
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 ...
🌐
Python.org
discuss.python.org › typing
Structural Pattern Matching for Types - Typing - Discussions on Python.org
April 10, 2025 - I’m sure that there are other use cases for this, but the main one that comes to mind is inferring the type of function parameters. This has been discussed at Extract kwargs types from a function signature (however I am proposing something more general). It is common that you want to override a superclass method, for example, which may accept many parameters that you don’t need in the override: class MyClass(BaseClass): def my_method(self, *args, **kwargs): ... return super(...
🌐
Real Python
realpython.com › structural-pattern-matching
Structural Pattern Matching in Python – Real Python
August 6, 2024 - Python compares the subject against each of your patterns to determine which branch of code to execute. The subject must match a given pattern exactly to make it succeed. Strings aren’t the only literals you can use in this kind of structural ...
🌐
Python Course
python-course.eu › python-tutorial › structural-pattern-matching.php
17. Structural Pattern Matching | Python Tutorial | python-course.eu
The match statement works by comparing an evaluated expression (also known as an "expression under test") with one or more patterns. A pattern is a special expression that corresponds to a particular data type or contains a complex data structure such as a list or a tuple.
🌐
Reddit
reddit.com › r/learnpython › how to use structural pattern matching to match built-in types?
r/learnpython on Reddit: How to use Structural Pattern Matching to match built-in types?
January 26, 2022 -

Hello,

I'm trying to use SPM to test if some variable is an int or a str and I can't seem to make it work.

The following code:

from typing import Type

def main(type_to_match: Type):
    match type_to_match:
        case str():
            print("This is a String")
        case int():
            print("This is an Int")
        case _:
            print("\nhttps://en.meming.world/images/en/0/03/I%27ve_Never_Met_This_Man_In_My_Life.jpg")

if __name__ == "__main__":
    test_type = str
    main(test_type)

Outputs: https://en.meming.world/images/en/0/03/I%27ve_Never_Met_This_Man_In_My_Life.jpg

I fail to see what I'm doing wrong.

EDIT: A very helpful SO user found a solution. Here it is, if you're interseted:

import builtins
from typing import Type


def main(type_: Type):
    match (type_):
        case builtins.str:  # it works with the dotted notation
            print(f"{type_} is a String")
        case builtins.int:
            print(f"{type_} is an Int")
        case _:
            print("\nhttps://en.meming.world/images/en/0/03/I%27ve_Never_Met_This_Man_In_My_Life.jpg")

if __name__ == "__main__":
    main(type("hello"))  # <class 'str'> is a String
    main(str)  # <class 'str'> is a String
    main(type(42))  # <class 'int'> is an Int
    main(int)  # <class 'int'> is an Int
🌐
ArjanCodes
arjancodes.com › blog › how-to-use-structural-pattern-matching-in-python
Introduction to Structural Pattern Matching in Python | ArjanCodes
June 20, 2024 - This means using the type_name() syntax, similar to instantiating an object. This is why I use int() instead of int. Sequence patterns match against lists, tuples, and other sequences.
🌐
Hillel Wayne
hillelwayne.com › post › python-abc
Crimes with Python's Pattern Matching
July 31, 2022 - The pattern matching is flexible but also fairly limited. It can only match on an object’s type, meaning we have to make a separate ABC for each thing we want to test. Fortunately, there’s a way around this. Python is dynamically typed.
🌐
Turingtaco
turingtaco.com › pattern-matching-lists-and-dictionaries-in-python
Pattern Matching Lists and Dictionaries in Python
December 7, 2024 - Pattern matching with tuples in Python operates under the same principles as lists, but we'll use parenthesis instead of square brackets. Besides that, the process has a crucial distinction of the underlying type.