Comment by w-m
3 years ago
Looking in the implementation of the Python version of ABC[0], you'll see that `__subclasshook__` doesn't do any language magic, it just registers the method to be called in `__instancecheck__`. So if you just implement `__instancecheck__` directly, you get the same behavior, but without the caching around it.
class OneWayMeta(type):
seen_classes = set()
@classmethod
def __instancecheck__(cls, instance):
C = instance.__class__
print(f"trying {C}")
if C in cls.seen_classes:
return False
cls.seen_classes |= {C}
return True
class OneWay(metaclass=OneWayMeta):
pass
def f(x):
match x:
case OneWay():
print(f"{x} is a new class")
case _:
print(f"we've seen {x}'s class before")
if __name__ == "__main__":
f("abc")
f([1, 2, 3])
f("efg")
When running:
trying <class 'str'>
abc is a new class
trying <class 'list'>
[1, 2, 3] is a new class
trying <class 'str'>
we've seen efg's class before
Am I missing a particular point the article is making, or did the author overlook this?
[0]: https://github.com/python/cpython/blob/main/Lib/_py_abc.py
> if you just implement `__instancecheck__` directly
The point of the article is that to override what "isinstance(obj, ClassA)" means one doesn't need to touch ClassA, or any descendants of it, at all.
Can you help me understand what you mean here? The author manages to make something match `case NotIterable()` by...modifying the `NotIterable(ABC)` class. That's exactly what I would have expected. What you mean by "one doesn't need to touch ClassA, or any descendants of it, at all."?
Not quite! The point of the article is that you don’t need to change the class of obj to override this instance check. If you have a look, ClassA always uses the hook implementation in the examples. And this can be shortened to just using instancecheck.
Right, but, what's the problem with that?
I think the point in the first section is that the way ABC instancecheck/subclasshook interacts with pattern matching is surprising for anyone not familiar with ABC. It allows you to check for a match with arbitrary functions, beyond simply checking if an object is an instance of a given type. In the final section where he has issues with caching, I presume he hasn't read about instancecheck, your code would fix his issue.