Search code examples
pythonpython-typingstructural-pattern-matching

Can you pattern match on Python type annotations?


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?


Solution

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