On Tue, Sep 14, 2021 at 4:33 PM Brandt Bucher <brandtbucher@gmail.com> wrote:
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`.

That seems like violating the Zen: "Errors should never pass silently." It would certainly have a ripple effect, since everyone who currently defines a __iter__ (in C or Python) that returns self would want to remove it, and documentation would need to be updated everywhere. I don't see this issue as important enough to do that. There are also probably multiple things that emulate iter() that would have to be updated to match, if builtin iter() starts changing its behavior.

TBH I don't think there is an *actual* problem here. I think it's just about choosing the right wording for the glossary (which IMO does not have status as a source of truth anyway).
 
> > 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__`.

(Did you mean the other way around? __iter__ without next is an Iterable but not an Iterator. And isinstance() returns the right answer for this.)
 
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...

The ABC Iterator does not define the concept Iterator though. And static type checking is not meant to exactly follow all the rules of the language anyway -- there are many approximations being made by static type checkers.

Regarding the meaning of "requires", not all requirements are checked at runtime either.

But I expect we won't be able to make everyone happy here.

--
--Guido van Rossum (python.org/~guido)