Chen A.
Chen A.

Reputation: 11338

How an iterable object is iterated without next?

I've done a research over similar questions on this subject, but didn't find a duplicate.

It is stated that an object is iterable if it implements __iter__ 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.

iterator.__next__(): Return the next item from the container. If there are no further items, raise the StopIteration exception.

From my understanding this applies to all iterator objects. I've encountered a code that implements a binary-tree container. The container only has __iter__ and so does the node objects resides in it.

The __iter__ implementation of the Node objects returns a generator. It yields objects and seems to do all the logic, without an implementation of __next__.

How this code actually works? It seems to function just as a regular iterator, but this one has no __next__. Ofcourse if I manually do iter(obj) and then next(obj) it works. Here is the code snippet:

class BinaryCont(object):

    def __init__(self):
        self.root = None
        self.size = 0

    def __iter__(self):

        class EmptyIter():
            def next(self):
                raise StopIteration

        if self.root:
            return self.root.__iter__()
        return EmptyIter()

class Node(object):

    def __init__(self, val, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

    def __iter__(self):
        if self.has_left_child():
            for child in self.left:
                yield child

        yield self.val

        if self.has_right_child():
            for child in self.right:
                yield child

An example of running code

bt = BinaryCont()
bt.insert(5)
bt.insert(3)
bt.insert(7)
for node in bt:
    print node

3
5
7

it = iter(bt)
type(it)
<type 'generator'>

Upvotes: 8

Views: 3061

Answers (2)

Martijn Pieters
Martijn Pieters

Reputation: 1125068

Your __iter__ method is a generator function, because it uses yield in the function body. A generator function, when called, returns a generator object. It is that object that has a __next__ method.

Your Node is not an iterator itself. It is merely an iterable object; an iterable object returns a new iterator instance when you call it's __iter__ method, which is what happens here.

Upvotes: 10

Mad Physicist
Mad Physicist

Reputation: 114578

A generator very much does have a __next__ method. You just don't implement it yourself. A "function" with a yield statement does not run your code. It returns an object that runs your code. That object has a __next__ method.

You may also want to note that there is one other mechanism for making an object iterable: providing __len__ and __getitem__ methods. In that case, iteration will occur over the indices from zero to len-1.

Upvotes: 3

Related Questions