Because ListCursor follows the iterator protocol, it already plugs into every part of Python that consumes iterators: for-loops, list(), tuple(), unpacking, and more. A for-loop is just repeated next calls under the hood, ending when StopIteration fires. Nothing new is needed for that to work, which is exactly the point of the pattern.
To make the cursor pull its weight, add a take(n) method: it returns up to n items as a list, drawing them out of the cursor and stopping cleanly if the cursor runs dry first. take is the building block a real client uses to consume a stream in bounded chunks.
class ListCursor:
def __init__(self, items):
self._items = list(items)
self._index = 0
def __iter__(self):
return self
def __next__(self):
if self._index >= len(self._items):
raise StopIteration
value = self._items[self._index]
self._index += 1
return value
def take(self, n):
"""Pull up to n items out of this cursor and return them as a list."""
# TODO: collect items by asking this cursor for its next value at most
# n times, gathering them in order into a list you return. A pull can
# fail because the cursor is already spent, so catch that and stop
# early: a short tail comes back short, and an empty list once dry.
pass
The editor and the test runner need a wide screen. Open this page on a desktop browser and your progress will be waiting.
Because ListCursor follows the iterator protocol, it already plugs into every part of Python that consumes iterators: for-loops, list(), tuple(), unpacking, and more. A for-loop is just repeated next calls under the hood, ending when StopIteration fires. Nothing new is needed for that to work, which is exactly the point of the pattern.
To make the cursor pull its weight, add a take(n) method: it returns up to n items as a list, drawing them out of the cursor and stopping cleanly if the cursor runs dry first. take is the building block a real client uses to consume a stream in bounded chunks.
Press Run Tests or ββ© to check your solution.