Class type pattern matching

I am comfused by this code and the brainstorming to figure out how to make it works:

from dataclasses import dataclass
import collections
import dataclasses

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 it work and not for A or int ???
        case A.AA:
            print("  is A.AA")
        case collections.Counter:
            print("  is Counter")
        case _:
            print("  _")
    match cls.__qualname__:
        case int.__qualname__:
            print("  is int")
        case str.__qualname__:
            print("  is str")
        case A.__qualname__:
            print("  is A")
        case A.AA.__qualname__:
            print("  is A.AA")
        case B.__qualname__:
            print("  is B")
        case _:
            print("  _")

print("Class A")
match_class_type(A)
print("Class B")
match_class_type(B)

print("Class A.AA")
match_class_type(A.AA)
print("collections.Counter")
match_class_type(collections.Counter)

for _ in dataclasses.fields(MyClass):
    print(f"{_.name} {_.type}")
    match_class_type(_.type)
1 Like

You stumbled upon the difference between Capture Patterns and Value Patterns.

When you have a snippet like

match cls:
    case A: ...

the A matches anything cls could be, and binds to the local variable A. The difference with case _ is that _ is a wildcard, and still matches anything but does not bind to a local variable. e.g. try printing _ vs A.

The reason why A.AA works is because it participates in Value Patterns. PEP635 describes the rationale, but there is effectively a rule that says dot accessors is supported. So case A.AA is syntactic sugar for case a if a == A.AA. This also shows you how you might support matching cls to A or int.