0

As noted here iterator objects are required to implement both __iter__ and __next__ methods.

The iterator objects themselves are required to support the following two methods, which together form the iterator protocol:

iterator.__iter__() Return the iterator object itself. This is required to allow both containers and iterators to be used with the for and in statements. This method corresponds to the tp_iter slot of the type structure for Python objects in the Python/C API.

iterator.__next__() Return the next item from the iterator. If there are no further items, raise the StopIteration exception. This method corresponds to the tp_iternext slot of the type structure for Python objects in the Python/C API.

By this definition in the below snippet, I didn't have implemented an iterator:

class Test:
    def __init__(self, size):
        self.size = size

    def __iter__(self):
        return TestIter(self.size)


class TestIter:

    def __init__(self, target=20):
        self.counter = 0
        self.target = target

    def __next__(self):
        if self.counter > self.target:
            raise StopIteration
        self.counter += 1
        return self.counter

Because neither Test nor TestIter has both __iter__ and __next__ methods defined. But the Test class has fully functionality of an iterator.

>>> for i in Test(5):
...     print(i)
... 
1
2
3
4
5
6
>>> 
>>> list(Test(5))
[1, 2, 3, 4, 5, 6]
>>> 
>>> it = iter(Test(5))
>>> next(it)
1
>>> next(it)
2
>>> next(it)
3
>>> next(it)
4
>>> next(it)
5
>>> next(it)
6
>>> next(it)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/tmp/iterators.py", line 18, in __next__
    raise StopIteration
StopIteration
>>> 

Is this technically correct that Test is not an iterator according to the definition?

Amir reza Riahi
  • 1,540
  • 2
  • 8
  • 34
  • Actually `Test` class does implement both of those methods. Next is quirky and non intuitive, but it is implemented. – Gameplay Mar 16 '23 at 09:01
  • You implemented an *iterable* `Test`. Ideally, `TestIter` should be an iterator, by implementing `__iter__` to `return self`. This is not required in order to iterate over a `Test` instance, but the result you get from its `__iter__` is supposed to be idempotent. – Karl Knechtel Mar 16 '23 at 09:03
  • The canonical version of the question - which covers that material and much more - is https://stackoverflow.com/questions/9884132. I would close this as a duplicate, but I am out of votes for today. – Karl Knechtel Mar 16 '23 at 09:04
  • @KarlKnechtel I got you. :) – Cow Mar 16 '23 at 09:05
  • @KarlKnechtel While the `Test` doesn't have both methods, why you think it's an iterable? I think it's clearly conflicting the defition. – Amir reza Riahi Mar 16 '23 at 09:07
  • Because "iterable" doesn't require a `__next__`. It requires that `__iter__` *returns something that has* `__next__`. The point of this is clear when you see how Python implements the `for` loop. – Karl Knechtel Mar 16 '23 at 09:09
  • @KarlKnechtel So I've mistakenly used the `iterable` in my comment. The question is about iterators not iterables. – Amir reza Riahi Mar 16 '23 at 09:11
  • Oh. Indeed, you did not, technically, implement an iterator. But you do not need to in order to make this code work. You *do* need to, in order to make `for i in iter(Test(5)):` work - and that is normally expected to work, and do the same thing that `for i in Test(5):` does. Again, **all** of this is covered by the other question I linked. – Karl Knechtel Mar 16 '23 at 09:13

0 Answers0