Crimes with Python's Pattern Matching (2022)

https://news.ycombinator.com/rss Hits: 8
Summary

One of my favorite little bits of python is __subclasshook__. Abstract Base Classes with __subclasshook__ can define what counts as a subclass of the ABC, even if the target doesn’t know about the ABC. For example: class PalindromicName(ABC): @classmethod def __subclasshook__(cls, C): name = C.__name__.lower() return name[::-1] == name class Abba: ... class Baba: ... >>> isinstance(Abba(), PalindromicName) True >>> isinstance(Baba(), PalindromicName) False You can do some weird stuff with this. Back in 2019 I used it to create non-monotonic types, where something counts as a NotIterable if it doesn’t have the __iter__ method. There wasn’t anything too diabolical you could do with this: nothing in Python really interacted with ABCs, limiting the damage you could do with production code. Then Python 3.10 added pattern matching. A quick overview of pattern matching From the pattern matching tutorial: match command.split(): case ["quit"]: print("Goodbye!") quit_game() case ["look"]: current_room.describe() case ["get", obj]: character.get(obj, current_room) You can match on arrays, dictionaries, and custom objects. To support matching objects, Python uses isinstance(obj, class), which checks If obj is of type class If obj is a transitive subtype of class If class is an ABC and defines a __subclasshook__ that matches the type of obj. That made me wonder if ABCs could “hijack” a pattern match. Something like this: from abc import ABC class NotIterable(ABC): @classmethod def __subclasshook__(cls, C): return not hasattr(C, "__iter__") def f(x): match x: case NotIterable(): print(f"{x} is not iterable") case _: print(f"{x} is iterable") if __name__ == "__main__": f(10) f("string") f([1, 2, 3]) But surely Python clamps down on this chicanery, right? $ py10 abc.py 10 is not iterable string is iterable [1, 2, 3] is iterable Oh. Oh my. Making it worse Pattern matching can also destructure object fields: match event.get(): case Click(position=(x, y)): handle_click_at(x, y) We can...

First seen: 2025-08-21 21:30

Last seen: 2025-08-22 04:48