1

Image we have this custom iterator class:

class Counter: 
             def __init__(self, low,high):
                    self.current=low-1 
                    self.high=high
              def __iter__(self):
                    return self
              def __next__(self):
                     self.current+=1
                     if self.current  < self.high:
                                return self.current
                      raise StopIterationError

Is it necessary to call iter() on the object and then call next() on it? (I don't want to use a for loop)

Obj=Counter(1,10)
i=iter(Obj)
print(next(i))

Or:

Obj=Counter(1,10)
print(next(Obj))

Both seem to work.

sophros
  • 14,672
  • 11
  • 46
  • 75
Sherafati
  • 196
  • 1
  • 10
  • 1
    Nope it' isn't ;) – azro Jan 17 '21 at 09:32
  • I mean, in what case we should call iter() explicitly? When we have some statements inside __ iter __? – Sherafati Jan 17 '21 at 09:33
  • 1
    *in what case* → when you want to implement a loop manually, rather than using `for`. – iBug Jan 17 '21 at 09:33
  • @iBug so in my third snippet i'm not using a for loop and have not used iter() either, but it still works – Sherafati Jan 17 '21 at 09:34
  • 2
    It's an iterator already. `iter()` is for obtaining an iterator from another "container" class (list, tuple, etc.). Implementing `__iter__` for an iterator class is for compatibility reasons. – iBug Jan 17 '21 at 09:35
  • `iter()` accepts a second `sentinal` argument, when `__next__` returns this value iteration is stopped – Iain Shelvington Jan 17 '21 at 09:37
  • @iBug how is it an iterator class without using __ iter __? Is it because of __ next __? – Sherafati Jan 17 '21 at 09:38
  • 1
    also check https://stackoverflow.com/questions/9884132/what-exactly-are-iterator-iterable-and-iteration – buran Jan 17 '21 at 09:39

1 Answers1

1

Creating a iterator (calling iter(...)) from an iterable that already is an iterator is a no-op. This explains why both of your snippets work the same way.

Now, what makes an iterator? It is the implementation of __next__ methods as you did. __iter__ magic function is used when you call iter on it. Typically, both of the methods are implemented together.

So, an instance of your class is already an iterator and there is no need to create one.

It is worth noting too, that typically iterators are just used in for loops where next or __next__ is called implicitly. So your test of iterator - next(i) is like one cycle of a for loop on the iterator.

sophros
  • 14,672
  • 11
  • 46
  • 75
  • Deleting __ iter __ from the class still makes it work. So i assume only __ next __ is what makes that class an iterator not __ iter __ – Sherafati Jan 17 '21 at 09:52
  • So why should i implement __ iter __ in my custom iterator class? – Sherafati Jan 17 '21 at 10:13
  • Because if your class is iterable (but is not inheriting this feature from the base class) it is customary to expect that you can turn it into iterator with `iter`. And many libraries might expect such a contract is held. – sophros Jan 17 '21 at 10:47