0

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?

Community
  • 1
  • 1
PiotrB
  • 133
  • 1
  • 10
  • And of course i found code fragment (in abc) which starts with: `ok = cls.__subclasshook__(subclass)` – PiotrB Mar 02 '17 at 17:33
  • 1
    Excuse me if I'm missing something, but can you not just replace `return NotImplemented` with `return False`? Is there something in PEP8 your trying to adhere to? – Christian Dean Mar 02 '17 at 17:34
  • @leaf: Thank you, it was too obvious. But why all examples returns only `True` or `NotImplemented`. – PiotrB Mar 02 '17 at 17:38
  • 1
    I believe that is because what the [Python]() standard calls for when implemented "magic methods". – Christian Dean Mar 02 '17 at 17:44

2 Answers2

3

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
    
MSeifert
  • 145,886
  • 38
  • 333
  • 352
  • Ad 1. Yes, when assume that is mechanism similar to compilation (or reflection) in other languages. If there is some doubt the class is correct subbclass then code shouldn't run or even compile (not in python). Ad 2. I understand it, it is code from link. – PiotrB Mar 02 '17 at 17:53
1

I think the good way to implement this:

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
            else:
                return False
        return NotImplemented


class A(Sized):
    pass


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


print(issubclass(A, Sized))  # False
print(issubclass(B, Sized))  # True

I think that when we assume that abc is mechanism similar to compilation (or reflection) in other languages we should return False. If there is some doubt the class is correct subbclass then code shouldn't run or even compile (not in python).

PiotrB
  • 133
  • 1
  • 10
  • Please don't just dump code, explain why this solves the question. Otherwise you might also want to consider to [accept](http://stackoverflow.com/help/accepted-answer) my answer which also contains the relevant informations + explanations. – MSeifert Mar 02 '17 at 18:06