On Mon, Nov 29, 2021 at 12:11:43AM -0500, David Mertz, Ph.D. wrote:
> On Sun, Nov 28, 2021, 11:43 PM Paul Bryan <pbr...@anode.ca> wrote:
> 
> > According to https://docs.python.org/3/glossary.html#term-iterator and
> > https://docs.python.org/3/library/stdtypes.html#typeiter, iterators must
> > implement the __iter__ method.
> 
> 
> From your first link:
> 
> CPython implementation detail: CPython does not consistently apply the
> requirement that an iterator define __iter__().

That comment is newly added to the documentation, it wasn't there in 3.9:

https://docs.python.org/3.9/glossary.html#term-iterator


and I don't think it should have been added. Rather than muddying the 
waters with a comment that CPython doesn't obey its own rules, I would 
rather we fixed the broken iterators so that they weren't broken.

For Python classes, all it needs is a one line method. For C classes, I 
presume it's a bit more complex, but not that much.

Does anyone know what builtin or stdlib objects iterators fail to 
implement `__iter__`? I haven't been able to find any -- all the obvious 
examples do (map, filter, reversed, zip, generators, list iterators, 
set iterators, etc).


>>> obj = iter(zip('', ''))
>>> obj is iter(obj)
True


The inconvenient truth here is that if you have an object that defines 
only `__next__`, you **cannot** iterate over it directly!


>>> class BrokenIterator(object):
...     def __next__(self):
...             return 1
... 
>>> for i in BrokenIterator():
...     print(i)
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'BrokenIterator' object is not iterable


How is that an iterator when it doesn't support iteration?

You can, sometimes, get away with such a broken iterator if you iterate 
over it *indirectly*, that it, you have a second class with an 
`__iter__` method which returns a BrokenIterator instance:

class Container(object):
    def __iter__(self):
        return BrokenIterator()

Now you can iterate over a Container instance:

for i in Container():
    print(i)
    break

but the moment that somebody tries to use that as an actual 
iterator, for example by skipping the first element:


it = iter(Container())
next(it, None)  # discard the first element
for i in it:
    print(i)


it will blow up in their face.

Would-be iterators that supply only `__next__` are broken.

If you go back to the original PEP that introduced iterators in Python 
2.1, it is clear:

"Classes can define how they are iterated over by defining an __iter__() 
method; this should take no additional arguments and return a valid 
iterator object. A class that wants to be an iterator should implement 
two methods: a next() method that behaves as described above, and an 
__iter__() method that returns self."

https://www.python.org/dev/peps/pep-0234/


The reference manual is correct. I quote:

"One method needs to be defined for container objects to provide 
iterable support: ..." (that would be `__iter__`).

"The iterator objects themselves are required to support the following 
two methods, which together form the iterator protocol: ..." (and they 
would be `__iter__` returning self, and `__next__`).

https://docs.python.org/3/library/stdtypes.html#iterator-types



> That said, I don't think the description at the link is very good.  Anyway,
> it's different from what I teach, 

Then you are teaching it wrong. Sorry.


> and also different from how Python
> actually behaves.  E.g.:
> 
> >>> class Foo:
> ...     def __iter__(self):
> ...         return Bar()

Then Foo instances are *iterable* but they are not iterators.


> ...
> >>> class Bar:
> ...     def __next__(self):
> ...         if random() > 0.5:
> ...             raise StopIteration
> ...         return "Bar"

And Bar instances are broken iterators.

[...]
> Or anyway, what would you call `bar := Bar()` if not "an iterator?!

A broken iterator which cannot be iterated over directly.



-- 
Steve
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/BMSP6CBPOOCDJQPPX5URUYOMIVLAMWGA/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to