-
-
Notifications
You must be signed in to change notification settings - Fork 30.4k
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
Add Collection to collections.abc and typing #71785
Comments
See the discussion on python-ideas entitled "An ABC representing "Iterable, Sized, Container"". |
I'm thinking that it should be called Collection after all. --Guido (mobile) |
Does all of the recognition patterns in typing.py also need to be in collections.abc? AFAICT, the current collections.abc classes for Sized, Iterable, and Container almost never get used (possibly because they don't provide any useful mixin methods). In the interest of maximizing the ratio of useful ABCs (like MutableMapping), could we defer expanding collections.abc until we've seen some real use cases? |
It would be a deviation from existing practice for a collection ABC to These ABCs, while not so useful by themselves, are building blocks for |
In putting together a patch for this, I noticed that the __subclasshook__ classmethods don't compose well. I'm unclear about the semantics about what these hooks are supposed to mean for subclasses and whether there is a bug in the existing code for Set, Sequence, and Mapping. Each of those inherits from Sized, Iterable, and Container, but only the Sized.__subclasshook__ is visible to the subclass. Also, the code for Sized.__subclasshook__ has an identity check for Sized which doesn't seem correct from the point-of-view of the subclasses. |
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.) |
I am way too late to the discussion but I also support the term "collections". I think it also helps developers coming from a C# background: |
Ping -- I'd really like to see this happening, with "Collection" as the name. |
@gvanrossum is there any reason that subclasshook is implemented by overriding instead of cooperation? E.g.,: class Sized(metaclass=ABCMeta):
@classmethod
def __subclasshook__(cls, C):
return (super().__subclasshook__(C) and
any("__len__" in B.__dict__ for B in C.__mro__)) etc. And then Collection does not need to implement subclasshook since its base classes cooperate? |
Why don't you give it a try? I'd be happy to review a patch from you. |
I thought about this some more. It won't work because when a class method I also don't see the use case (you're not supposed to do this at home, |
I submit a simple solution in line with __subclasshook__ of other ABCs. Please review. |
LGTM, but I'm on vacation through Monday. |
Great patch. Shouldn't Sequence be a "Reversible, Collection"? |
(added the documentation changes) |
Thank you Neil, I agree that Sequence is a reversible collection. I combined you documentation patch with updated _collections_abc and updated test_functools (it tests MRO) into a single patch. |
Given issue http://bugs.python.org/issue27802, it might be worth considering that all Collections implement __eq__ and __ne__, so maybe these should be abstract methods on Collection? |
(never mind about the comparison operators :) Turns out that would break backwards compatibility.) |
No on adding __eq__. If another core dev is available please go ahead and commit. |
I still believe "Reiterable" better demonstrates the concept. When you request a Reiterable as a function parameter or assert if something is a Reiterable the other side knows exactly what you mean. A "Collection" is way more ambiguous - if you create an object that acts like range() but you can cycle over it more than once I wouldn't exactly call it a collection. I would though call it a Reiterable and it would be clear for any Python programmer familiar with the concept of iterators. I believe this is a funny case in which the naming is more important than the implementation as it will turn into a term or concept that will be further used in many places to come. |
ReIterable and Collection are different concepts.
If we had ReIterable I would probably decree that Collection inherits from it (practicality beats purity). But I don't think ReIterable by itself would do much good; apart from a few "show-off" hacks, any reasonable ReIterable would also be able to implement __contains__ and __len__ easily. And I would certainly call range() a Collection. |
New changeset acd9a465b373 by Guido van Rossum in branch 'default': |
I'll keep this open until I've also merged typing.py. |
changeset: 102867:355fa8bd8d9d changeset: 102868:013bb690c24a |
Actually there's one TODO left: the docs for typing (at least in 3.6) |
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: