19
def __iter__(self):
    return self 

Just wanted to know what does the above code do in general, why is it required.

I went through many code tutorial and blocks, was getting mutiple answers without any proper specification, just a brief explanation would be great.

Vincent Savard
  • 34,979
  • 10
  • 68
  • 73
Smple_V
  • 838
  • 3
  • 13
  • 32
  • 1
    Possible duplicate of [How to make class iterable?](http://stackoverflow.com/questions/19151/how-to-make-class-iterable) – vaultah Feb 26 '16 at 17:22
  • 1
    @vaultah ya its from the iterable code section, I have seen this section in many other methods, so I just wanted to know why we use this part of code in general – Smple_V Feb 26 '16 at 17:25
  • What do you mean? Did you read the answer on the linked page? *"The `__iter__` returns the iterator object and is implicitly called at the start of loops"* is the answer to your question. – vaultah Feb 26 '16 at 17:26
  • yes I have read it, and just like others it ain't clearing the logic – Smple_V Feb 26 '16 at 17:29
  • Fluent Python, chapter 14 → http://www.amazon.com/Fluent-Python-Luciano-Ramalho/dp/1491946008/ – Arĥimedeς ℳontegasppα ℭacilhας Feb 26 '16 at 21:39

3 Answers3

9

EDIT: for 2.7 instead of 3

Here is my understanding

In below example code, we can say that class Testing is an iterable object because we implemented it with __iter__. Method __iter__ returns an iterator. The iterator uses the next method to determine the next value on the iteration. If I were to remove the next method from the class below, the code would fail.

iterable = an object that can be iterated over...implemented with __iter__

iterator = object that defines how to iterate...literally, what is the next value. This is implemented with __next__

So the piece of code you questioned actually takes the class object (self is the argument) and returns an iterator, which makes the class object iterable. So in the example below we can actually iterate over the class object myObj.

class Testing:
    def __init__(self,a,b):
        self.a = a
        self.b = b
    def __iter__ (self):
        return self
    def next(self):
        if self.a <= self.b:
            self.a += 1
            return self.a-1
        else:
            raise StopIteration

myObj = Testing(1,5)           
for i in myObj:
    print i
yoyoyoyo123
  • 2,362
  • 2
  • 22
  • 36
9

It's a bit confusing. In short, defining __iter__ to return self is essentially turning an iterator object into an iterable quickly so that you can use it in a for loop.

Recall that an iterator is an object with a next method for the purpose of returning the next item in the process of performing a iteration. After the next method is called, the state of the iterator changes.

Recall that an iterable is an object with an __iter__ method, whose purpose is to return a fresh iterator to perform a iteration.

To separate the concepts, let's take the example of the iterator OneToFourIterator and the iterable OneToFourIterable

class OneToFourIterator:
  def __init__(self):
    self.state = 0
  def next(self):
    self.state += 1
    if self.state > 4:
      raise StopIteration
    return self.state

class OneToFourIterable:
  def __init__(self):
    pass
  def __iter__(self):
    return OneToFourIterator()

To perform a iteration using a for loop, you have to give it an iterable like this,

for i in OneToFourIterable():
  print i

If you use an iterator in a for loop, it will give an error. To use an iterator for iteration, you would do something like this

iterator = OneToFourIterator()
while True:
  try:
    current = iterator.next()
    print current
  except StopIteration:
    break

Finally, I think the answer to why people define __iter__ to return self, is that instead of writing 2 classes like the above, you can quickly turn an iterator into an iterable so that you can use it in a for loop:

class OneToFourIteratorAndIterable:
  def __init__(self):
    self.state = 0
  def next(self):
    self.state += 1
    if self.state > 4:
      raise StopIteration
    return self.state
    pass
  def __iter__(self):
    return self

for i in OneToFourIteratorAndIterable():
  print i
user2191332
  • 1,059
  • 3
  • 17
  • 24
  • 2
    To be iterable, the object that is returned by ```__iter__``` must have a ```__next__``` method defined. This is why iterator classes return self in the ```__iter__```. It's because the class defines the ```__next__``` – georgwalker45 Apr 05 '23 at 17:05
  • Does this mean returning `self` in `__iter()__` is actually incorrect? Because `self` is not necessarily a new iterator in a clean state. I just tried it out and it seems so. – lucidbrot May 16 '23 at 08:29
3

It also confuse me for a little time :) ok,let's see a for loop in python:

for i in a:
    print i

In this snippet, the for loop will implicitly calls a.iter(),which should be an iterator.What's more ,in python, if you want an iterator ,the class must implement 'the iterator' interface, which means it should have both__iter__ and__next__ , and the effect of __iter__is return an iterator.

However, there is another concept in python that calls iterable,it is a factory which can pruduct iterators.

OK,now,what we want is can iter either iterator or iterable, so, their iter method must return a iterator.

for iterable,it should return an iterator(probably another class implement 'iterator' interface), and for iterator, yeah, itself is an iterator, so just return itself!

the following snippet is copy from Fluent Python:

import reprlib

RE_WORD = re.compile('\w+')


class Sentence:
    def __init__(self, text):
        self.text = text
        self.words = RE_WORD.findall(text)

    def __repr__(self):
        return 'Sentence(%s)' % reprlib.repr(self.text)

    def __iter__(self):
        return SentenceIterator(self.words)


class SentenceIterator:
    def __init__(self, words):
        self.words = words
        self.index = 0

    def __next__(self):
        try:
            word = self.words[self.index]
        except IndexError:
            raise StopIteration()
        self.index += 1
        return word

    def __iter__(self):
        return self
s = Sentence('"The time has come," the Walrus said,')

you can do:

   for i in iter(s):
       print(s)

and:

   for i in s:
       print(s)

if you remove __iter__in iterator,you can only use:

 for i in s:
     print(s)

Because iter(s) is no more an iterator when you remove __iter__in iterator class

dogewang
  • 648
  • 1
  • 7
  • 15
  • 1
    Hmm, are you sure about the statement:if you delete __iter__ in iterator, that the for loop still works? Mine certainly doesn't... Otherwise, the above answers are great. – user7865286 Mar 29 '18 at 09:40
  • @user7865286 yes,you can remove __iter__ in iterator, in above code snippet,SentenceIterator is the iterator,you can remove it and run 'for i in s:print s'. – dogewang Mar 30 '18 at 02:14