Author gvanrossum
Recipients brett.cannon, gvanrossum, rhettinger
Date 2016-07-25.00:36:20
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1469406983.21.0.53798762066.issue27598@psf.upfronthosting.co.za>
In-reply-to
Content
Regarding the design of __subclasshook__, these two flaws kind of cancel each other out. The idea is that if you have a "one-trick pony" class whose issubclass() check must verify that the purported sublass implements a specific method (e.g. __hash__), you don't want that subclass check to be inherited by a subclass. I suppose perhaps this ought to have been done by making ABCMeta.__subclasscheck__ check for the __subclasshook__ in the class dict, but (for reasons I've forgotten) it's not doing it that way, instead just calling cls.__subclasscheck__. All known __subclasshook__ implementations (those in collections.abc anyway :-) compensate for this by returning NotImplemented if the class for which they are being called isn't exactly the class in which they are defined. The final piece of the puzzle is object.__subclasshook__(), which always returns NotImplemented.

(NOTE: When reading the above paragraph, be careful to distinguish between __subclasscheck__, which is invoked by issubclass() and isinstance(), and __subclasshook__, which is only invoked by ABCMeta.__subclasscheck__().)

Here's an example showing why we don't want __subclasshook__ to be inherited. Suppose we have a class SupportsInt, like this:

class SupportsInt(ABC):
    @classmethod
    def __subclasshook__(cls, C):
        return hasattr(C, '__int__')

Now suppose we had a concrete class inheriting from this:

class MyInteger(SupportsInt):
    def __int__(self):
        return 0

Now, alas, everything with an __int__ method is considered to be a MyInteger, for example isinstance(0, MyInteger) returns True.

So what should Collection.__subclasshook__ do? It could do something like this:

class Collection(Set, Iterable, Container):
    @classmethod
    def __subclasshook__(cls, C):
        if cls is not Collection:
            return NotImplemented
        for base in Set, Iterable, Container:
            ok = base.__subclasshook__(C)
            if ok != True: return False
        return True

(Untested, hopefully you get the idea. Hope this helps.)
History
Date User Action Args
2016-07-25 00:36:23gvanrossumsetrecipients: + gvanrossum, brett.cannon, rhettinger
2016-07-25 00:36:23gvanrossumsetmessageid: <1469406983.21.0.53798762066.issue27598@psf.upfronthosting.co.za>
2016-07-25 00:36:23gvanrossumlinkissue27598 messages
2016-07-25 00:36:20gvanrossumcreate