Curtwagner1984
Curtwagner1984

Reputation: 2088

How to use python's Structural Pattern Matching to test built in types?

I'm trying to use SPM to determine if a certain type is an int or an str.

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)

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

Most of the documentation I found talks about how to test if a certain variable is an instance of a type. But not how to test if a type is of a certain type.

Any ideas on how to make it work?

Upvotes: 11

Views: 19347

Answers (4)

The accepted answer gives a good solution for simple built in types (like str and int), but that doesn't work for lists, as their type is, for instance list[str], so a direction match with builtins.list is False. This works:

import typing

def test(self, type_: typing.Type):
    match typing.get_origin(type_) or type_:  # for instance str[int]->str, and or for str->str
        case builtins.bool:
            print('This is a bool')
        case builtins.str:
            print('This is a str')
        case builtins.int:
            print('This is an int')
        case builtins.float:
            print('This is a float')
        case td if typing.is_typeddict(type_):
            print('This is a TypedDict')
        case builtins.dict:
            print('This is a dict')
        case builtins.list:
            print('This is a list')
        case _:
            print(f'({type_}) unmatched')

Upvotes: 0

Joshua Little
Joshua Little

Reputation: 308

One option is to grab the type name and match on that instead of on the type directly:

from typing import Type

def main(type_to_match: Type):
    match type_to_match.__name__:
        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)

Upvotes: 2

Tomerikoo
Tomerikoo

Reputation: 19403

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")

Upvotes: 6

Patryk Bratkowski
Patryk Bratkowski

Reputation: 565

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

Upvotes: 22

Related Questions