First of all, we should ping Yury, who implemented `async for` about 6 years ago (see PEP 492), and Joshua Bronson, who implemented aiter() and anext() about 5 months ago (see https://bugs.python.org/issue31861). I've CC'ed them here.

My own view:

A. iter() doesn't check that the thing returned implements __next__, because it's not needed -- iterators having an __iter__ methor is a convention, not a requirement. You shouldn't implement __iter__ returning something that doesn't implement __iter__ itself, because then "for x in iter(a)" would fail even though "for x in a" works. But you get an error, and anyone who implements something like that (or uses it) deserves what they get. People know about this convention and the ABC enforces it, so in practice it will be very rare that someone gets bitten by this.

B. aiter() shouldn't need to check either, for exactly the same reason. I *suspect* (but do not know) that the extra check for the presence of __iter__ is simply an attempt by the implementer to enforce the convention. There is no *need* other than ensuring that "async for x in aiter(a)" works when "async for x in a" works.

Note that PEP 525, which defines async generators, seems to imply that an __aiter__ returning self is always necessary, but I don't think it gives a reason.

I do notice there's some backwards compatibility issue related to __aiter__, alluded to in both PEP 492 (https://www.python.org/dev/peps/pep-0492/#api-design-and-implementation-revisions) and PEP 525 (https://www.python.org/dev/peps/pep-0525/#aiter-and-anext-builtins). So it's *possible* that it has to do with this (maybe really old code implementing the 3.5 version of __aiter__ would be caught out by the extra check) but I don't think it is. Hopefully Yury and/or Joshua remembers?

FWIW I don't think there are any optimizations that avoid calling __iter__ or __aiter__ if __next__ or __anext__ is present. And certainly I wouldn't endorse adding them (this would seem an ad-hoc optimization that could break user expectations unexpectedly, quite apart from the issue discussed here).

--Guido

On Wed, Sep 1, 2021 at 4:11 PM Nick Coghlan <ncoghlan@gmail.com> wrote:
On Tue, 31 Aug 2021, 2:52 am Brett Cannon, <brett@python.org> wrote:

On Sun, Aug 29, 2021 at 2:01 PM Serhiy Storchaka <storchaka@gmail.com> wrote:

> So my question is whether the discrepancy between what `async for`
> expects and what `aiter()` expects on purpose?
> https://bugs.python.org/issue31861 <https://bugs.python.org/issue31861>
> was the issue for creating aiter() and I didn't notice a discussion of
> this difference. The key reason I'm asking is this does cause a
> deviation compared to the relationship between `for` and `iter()` (which
> does not require `__iter__` to be defined on the iterator, although
> collections.abc.Iterator does). It also makes the glossary definition
> being linked from
> https://docs.python.org/3.10/reference/compound_stmts.html#the-async-for-statement
> <https://docs.python.org/3.10/reference/compound_stmts.html#the-async-for-statement>
> incorrect.

PyIter_Check() only checks existence of __next__, not __iter__ (perhaps
for performance reasons).

Or maybe no one thought to require __iter__ for iterators?

I don't think PyIter_Check is testing the formal definition of an iterator, I think it's just testing if calling __iter__ can be skipped (as you say, for performance reasons).

I'm surprised iter() would skip calling __iter__ just because an object defines __next__, though. Even though "__iter__ is defined and returns self" is part of the iterator definition, it still feels like a leap from there to "if __next__ is defined, skip calling __iter__ in iter()".

The optimisation that bypasses the __[a]iter__ method call feels more legitimate in the actual for loop syntax, it just feels odd to me if the builtin isn't forcing the call.

Cheers,
Nick.



 
_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-leave@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/5UMLDQ4CANKY4WM6RNG67AEJMQI44X42/
Code of Conduct: http://python.org/psf/codeofconduct/


--
--Guido van Rossum (python.org/~guido)
Pronouns: he/him (why is my pronoun here?)