Guido van Rossum wrote:
On Tue, Sep 14, 2021 at 3:49 PM Brandt Bucher brandtbucher@gmail.com wrote:
I think it's also worth noting that a missing "`__iter__` that returns self" is trivial to recover from... just use a new reference to the iterator instead. The overhead of a method call for this convention almost seems silly. The use case is this:
Yeah, I understand that. But what I'm hinting that is that the `GET_ITER` opcode and `iter` builtin *could* gracefully handle this situation when called on something that doesn't define `__iter__` but does define `__next__`. Pseudocode: def iter(o): if hasattr(o, "__iter__"): return o.__iter__() elif hasattr(o, "__next__"): # Oh well, o.__iter__() would have just returned o anyways... return o raise TypeError This would be implemented at the lowest possible level, in `PyObject_GetIter`.
What worries me most about changing the current "requirement" is that it may create either confusion or backward compatibility issues for `collections.abc.Iterator` (which is a subtype of `Iterable`, and thus requires `__iter__`). If you explicitly inherit from Iterator, you inherit a default implementation of __iter__ (that returns self, of course). If you merely register, it's up to you to comply. And sometimes people register things that don't follow the letter of the protocol, just to get things going. (This is common for complex protocols like Mapping, where some function you have no control over insists on a Mapping but only calls one or two common methods.
Yeah, I was thinking about cases like `isinstance(o, Iterator)`, where `o` defines `__iter__` but not `__next__`. Even though this code might start returning the "right" answer, it's still a backward-compatibility break. Not sure what the true severity would be, though...