Card 04/ 09
Iterator Example: Paging Through API Results
The caller loops over orders; the iterator fetches the next page when the current one runs out.
Results fetched from a server a page at a time are a natural Iterator, and this example maps the template onto them. An order history holds hundreds of orders, and the server returns a few per request, with a token for the next page. The caller should loop over orders and never see a page or a token. The pages here are kept in a dictionary, so the listing runs without a network.
| Template | Paging example |
|---|---|
| ConcreteAggregate | OrderHistory |
| ConcreteIterator | OrderPageIterator |
| Position | The current page, the position within it, and the next page's token |
| End of the collection | A page with no next token whose orders have all been returned |
from collections.abc import Iterator
PAGES = {
None: (["order-1", "order-2"], "p2"),
"p2": (["order-3", "order-4"], "p3"),
"p3": (["order-5"], None),
}
def fetch_page(token: str | None) -> tuple[list[str], str | None]:
print(f" fetching page {token or 'p1'}")
return PAGES[token]
class OrderHistory:
def __iter__(self) -> "OrderPageIterator":
return OrderPageIterator()
class OrderPageIterator(Iterator[str]):
def __init__(self) -> None:
self._page, self._next_token = fetch_page(None)
self._position = 0
def __next__(self) -> str:
if self._position == len(self._page):
if self._next_token is None:
raise StopIteration
self._page, self._next_token = fetch_page(self._next_token)
self._position = 0
order = self._page[self._position]
self._position += 1
return order
for order in OrderHistory():
print(order) fetching page p1
order-1
order-2
fetching page p2
order-3
order-4
fetching page p3
order-5The loop at the bottom is an ordinary for, and prints five orders. The indented lines show when each page is fetched: page two is requested only after the second order has been returned, and page three after the fourth. A caller that stops after the first order would never cause the second request.
The position is now three values instead of one index, and none of them reaches the caller. If the server changed from page tokens to numbered pages, OrderPageIterator would change and the loop would not.
← → move through sections and cards