Python – How to Pattern Match on Python Type Annotations

pythonpython-typingstructural-pattern-matching

Can you pattern match on Python types?

I've seen simple examples:

import builtins

match x:
    case builtins.str:
        print("matched str")
    case buildins.int:
        print("matched int")

But I'd like to pattern match on a nested type, something like Annotated[Optional[Literal["a", "b", "c"]], "something here"] – is this possible?

Best Answer

Pattern matching is fundamentally compiled down to a chained series of isinstance and == calls, among others. This means that, if you can express Annotated[Optional[Literal["a", "b", "c"]], "something here"] as a series of isinstance, ==, etc. calls, you can do pattern matching.

The problem with trying to do pattern matching on items from typing is that the types of the objects typing.Annotated[...], typing.Optional[...], etc. are implementation details (they're subclasses of typing._GenericAlias, and their instance variables and properties are not public), whereas pattern matching (specfically class patterns) is a style which ideally works with programming against a public API. If you don't care that you're accessing implementation details, then this could work:

import types
from typing import *

x = Annotated[Optional[Literal["a", "b", "c"]], "something here"]

AnnotatedAlias = type(Annotated[Any, Any])
UnionAlias = type(Union[Any, None])
LiteralAlias = type(Literal[None])

match x:
    case AnnotatedAlias(
        __args__=(
            UnionAlias(__args__=(LiteralAlias(__args__=("a", "b", "c")), types.NoneType)),
        )
    ):
        print("x matched")