Reputation: 3
class Node:
def __init__(self, value):
self._value = value
self._children = []
def __repr__(self):
return 'Node({!r})'.format(self._value)
def add_child(self, node):
self._children.append(node)
def __iter__(self):
return iter(self._children)
def depth_first(self):
yield self
for c in self:
yield from c.depth_first()
if __name__ == '__main__':
root = Node(0)
child1 = Node(1)
child2 = Node(2)
root.add_child(child1)
root.add_child(child2)
child1.add_child(Node(3))
child1.add_child(Node(4))
child2.add_child(Node(5))
for a in root.depth_first():
print(a)
# Outputs Node(0), Node(1), Node(3), Node(4), Node(2), Node(5)
I thought that a list is an object that we can iterate over it so why use iter()
? I am new in python so this look to me so weird.
Upvotes: 0
Views: 3038
Reputation: 26
Every iterable object implements an iter() function that returns itself. the iter(obj) calls the obj.iter(), which is same as returning the list object in your example.
Upvotes: 0
Reputation: 160677
Because returning self._children
returns a list
object, which doesn't work as an iterator, remember, iterators implement the __next__
method in order to supply items during iteration:
>>> next(list()) # doesn't implement a __next__ method
TypeError: 'list' object is not an iterator
list
s are iterable, they can be iterated through because calling iter
on the will return an iterator, but, list
s themselves are not iterators -- a good breakdown of these can be found in the top answer of this Question.
A lists __iter__
method returns a custom new list_iterator
object each time __iter__
is called:
list().__iter__()
Out[93]: <list_iterator at 0x7efe7802d748>
and, by doing that, supports iteration multiple times, the list_iterator
object implements the __next__
method that's required.
Calling and returning iter
on it will just do that, return the lists iterator and save you the trouble of having to implement __next__
.
Addressing your comment as for why for c in self._children
works, well, because it is essentially doing the same thing. What basically happens with the for loop is:
it = iter(self._children) # returns the list iterator
while True:
try:
i = next(it)
<loop body>
except StopIteration:
break
meaning, iter
is called again on the list object and is used by the for
loop for next
calls.
Upvotes: 1