New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
doc: issubclass without registration only works for "one-trick pony" collections ABCs. #68052
Comments
The collections.abc documentation implies that *any* of the container ABCs can be used in an issubclass test against a class that implements all abstract methods:
In reality this only applies to the "One Trick Ponies" (term from PEP-3119, things like Container and Iterable, those classes with one or two methods). It fails for the compound container ABCs: >>> from collections.abc import Sequence, Container, Sized
>>> class MySequence(object):
... def __contains__(self, item): pass
... def __len__(self): pass
... def __iter__(self): pass
... def __getitem__(self, index): pass
... def __len__(self): pass
...
>>> issubclass(MySequence, Container)
True
>>> issubclass(MySequence, Sized)
True
>>> issubclass(MySequence, Sequence)
False That's because the One Trick Ponies implement a __subclasshook__ method that is locked to the specific class and returns NotImplemented for subclasses; for instance, the Iterable.__subclasshook__ implementation is: @classmethod
def __subclasshook__(cls, C):
if cls is Iterable:
if any("__iter__" in B.__dict__ for B in C.__mro__):
return True
return NotImplemented The compound container classes build on top of the One Trick Ponies, so the class test will fail, NotImplemented is returned and the normal ABC tests for base classes that have been explicitly registered continues, but this won't include unregistered complete implementations. Either the compound classes need their own __subclasshook__ implementations, or the documentation needs to be updated to make it clear that without explicit registrations the issubclass() (and isinstance()) tests only apply to the One Trick Ponies. |
I should have added the mixin methods for the Sequence implementation; the more complete demonstration is: >>> from collections.abc import Sequence, Container, Sized
>>> class MySequence(object):
... def __contains__(self, item): pass
... def __len__(self): pass
... def __iter__(self): pass
... def __getitem__(self, index): pass
... def __len__(self): pass
... def __reversed__(self): pass
... def index(self, item): pass
... def count(self, item): pass
...
>>> issubclass(MySequence, Container)
True
>>> issubclass(MySequence, Sized)
True
>>> issubclass(MySequence, Sequence)
False |
This does apply to all versions of Python from 2.6 up. Registering does work of course. I believe the reason for not having the __subclasshook__ is the following sentence in PEP-3119: "ABCs are intended to solve problems that don't have a good solution at all in Python 2, such as distinguishing between mappings and sequences." This used to be worse in <3.3 because there if you ever inherit from (By the way, if Py2 documentation is fixed, it should also say that these ABCs are new as of 2.6, not since 2.4 like the rest of the collections module). |
Probably I'm overlooking something, but why isn't this hook defined cooperatively, with a terminating base class method that returns True? If the call chain progresses to the base, then all of the interfaces have been satisfied. Otherwise one of the bases returns NotImplemented. If it's implemented cooperatively, then the |
I have added a failing test to based on the first example, of a class that provides the necessary methods, but fails to be an instance of Sequence. |
Guido, could we add those missing __subclasshook__ for consistency? |
No, I consider this is a documentation problem. I don't recall why the docs say that (I don't even know if they still say that or whether Martijn misread them), but IMO this should not be changed. |
This isn't meant as a comment from any previous posts. It's simply meant to correct a statement (based on new information in the past 2 years) from the original post. Since this original report, some ABCs that are not "One Trick Ponies" have been added which implement subclasshook. >>> from collections.abc import Sequence, Container, Sized, Collection
>>> class MySequence(object):
... def __contains__(self, item): pass
... def __len__(self): pass
... def __iter__(self): pass
... def __getitem__(self, index): pass
... def __len__(self): pass
... def __reversed__(self): pass
... def index(self, item): pass
... def count(self, item): pass
...
>>> issubclass(MySequence, Container)
True
>>> issubclass(MySequence, Sized)
True
>>> issubclass(MySequence, Sequence)
False
>>> issubclass(MySequence, Collection)
True Collection is not a "One Trick Pony" because it is used for Sized, Iterable Containers. Generator, Coroutine, and ASyncGenerator are also not "One Trick Ponies" (although they are defined under that section in _collections_abc.py). Again, for reference, the definition of One Trick Pony from PEP-3119 is: If only One Trick Ponies implemented __subclasshook__, then the original documentation issue:
maybe could have been changed to:
But, that's not really correct anymore. |
See also bpo-35190. |
Maybe bpo-45024 is also connected? (It also seems to relate to hasattr() logic.) |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: