Queue.__iter__ with a generator
Recall the weaknesses of using a
separate iterator class:
- Labor Intensive
- Poor Encapsulation
- Fragile Iteration
The definition
class Queue (object):
# Unmentioned methods identical to previous implementation
def __iter__ (self):
current_node = self._head
while current_node is not None:
item = current_node[0]
yield item
current_node = current_node[1]
# We no longer require a separate QueueIterator class
The complete implementation
Weaknesses resolved
- It's no longer labor intensive because the __iter__ method is
short, simple, and self-contained.
- Because it is self-contained, there are no longer encapsulation
issues. If Queue's implementation changes, there are no other
classes to worry about.
- Queue can guarantee that iterators will remain consistent after a
dequeue of an element that has already been iterated over, and
that any elements enqueued before StopIteration is raised will
also be iterated over.
- That is, the following code is legal (even though the first
part is kind of silly, as indicated in the comments):
# queue is a Queue with integers in it
even_queue = Queue()
# Note: this code is for illustrative purposes. The following two lines
# are more sensibly expressed as:
# while len(queue) > 0:
# num = queue.dequeue()
for num in queue:
queue.dequeue()
if num % 2 == 0:
even_queue.enqueue(num)
else:
queue.enqueue(randint(0, 10))
Generators are good!
We already know that iterators are good -- they allow us to define a
simple, consistent iteration pattern for our special functions or
collection classes. Hopefully it is now also apparent that using
generators greatly simplifies the process of defining iterators, making
our code clear and simple.
itertools uses generators