Search code examples
pythonpython-3.xabc

python abc subclasshook has no effect when class is derived


There is no way to return False from issubclass when class is derived from class with __subclashook__ implementation. I modified code from: python subclasscheck & subclasshook I only added '(Sized)' to both class definitions:

from abc import ABCMeta

class Sized(metaclass=ABCMeta):
    @classmethod
    def __subclasshook__(cls, C):
        if cls is Sized:
            if any("__len__" in B.__dict__ for B in C.__mro__):
                return True
        return NotImplemented

class A(Sized):
    pass

class B(Sized):
    def __len__(self):
        return 0

print(issubclass(A, Sized))  # True - should be False
print(issubclass(B, Sized))  # True

Is there any way to return False in this case? Or maybe I'm doing something wrong?


Solution

  • The problem is that you return NotImplemented when the __subclasshook__ doesn't exit early. And as stated in the documentation:

    If it returns NotImplemented, the subclass check is continued with the usual mechanism.

    So it uses the normal subclass check and finds that you do, in fact, inherit from Sized so returns True.

    There are two solutions:

    1. return False instead of return NotImplemented. However, do you really want/need issubclass to return False for direct subclasses?

    2. If you inherit from object for classes A and B it works as expected:

      class A(object):
          pass
      
      class B(object):
          def __len__(self):
              return 0
      
      print(issubclass(A, Sized))  # False
      print(issubclass(B, Sized))  # True