Reputation: 11338
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
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
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