← Back to context

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.

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.