Use when you need to access elements of a collection sequentially without exposing its underlying representation — decoupling traversal logic from the collection's data structure.
Scanned 9/8/2026
Install to Claude Code
npx -y skills add jeffreytse/grimoire-core --skill apply-iterator-pattern --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Apply Iterator Pattern?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/jeffreytse-apply-iterator-pattern)More formats (shields.io, HTML) on the badges page.
---
name: apply-iterator-pattern
description: Use when you need to access elements of a collection sequentially without exposing its underlying representation — decoupling traversal logic from the collection's data structure.
source: "Gamma, Helm, Johnson, Vlissides, \"Design Patterns: Elements of Reusable Object-Oriented Software\" (1994) pp. 257–271; Python iteration protocol (__iter__/__next__); Java Iterable/Iterator; C++ STL iterators; Ruby Enumerable"
tags: [design-patterns, behavioral, iterator, oop, developer, collection-traversal, encapsulation]
related: [apply-composite-pattern, apply-visitor-pattern, apply-solid-principles]
---
# Apply Iterator Pattern
Provide a way to sequentially access elements of a collection without exposing its underlying structure.
## Why This Is Best Practice
**Adopted by:** Python's iteration protocol (`__iter__`/`__next__` — every `for` loop
uses it, making it the most-invoked pattern in the language), Java's `Iterable`/
`Iterator` (every Java collection implements it — `for-each` loops, streams, and
collectors all depend on it), C++ STL iterators (the foundation of the entire Standard
Template Library algorithm set), and Ruby's `Enumerable` module.
**Impact:** Python's iterator protocol is cited in the language reference as the reason
generators, comprehensions, and the `for` statement all share one protocol. The unifying
effect: any object implementing `__iter__` works with `for`, `zip`, `map`, `list()`,
and every standard library function — without those functions knowing the collection's
type.
**Why best:** The alternative — index-based traversal — exposes the collection type
(`list[i]` doesn't work on a linked list or a file stream). Iterator abstracts over
any sequential structure, enabling algorithms that work on lists, trees, streams,
database cursors, and network responses uniformly.
Sources: Gamma et al. (1994) pp. 257–271; Python iterator protocol documentation;
Java `Iterable` specification
## Steps
### Step 1: Implement Python's iterator protocol on your collection
```python
class NumberRange:
def __init__(self, start: int, end: int, step: int = 1):
self._start = start
self._end = end
self._step = step
def __iter__(self):
current = self._start
while current < self._end:
yield current
current += self._step
```
Using `yield` creates a generator iterator — the simplest correct implementation.
### Step 2: For stateful iterators with external control, use `__next__` explicitly
```python
class NumberRangeIterator:
def __init__(self, start: int, end: int, step: int):
self._current = start
self._end = end
self._step = step
def __iter__(self):
return self
def __next__(self):
if self._current >= self._end:
raise StopIteration
value = self._current
self._current += self._step
return value
```
Use explicit `__next__` when the iterator must be paused and resumed externally
(e.g., paginated API responses loaded one page at a time).
### Step 3: Keep the iterator separate from the collection for multiple simultaneous traversals
```python
class BookShelf:
def __init__(self):
self._books: list[str] = []
def add(self, book: str):
self._books.append(book)
def __iter__(self):
return iter(self._books) # each call creates a fresh iterator
```
`iter(self._books)` creates a new list iterator each time, so two simultaneous
`for` loops over the same `BookShelf` don't interfere.
### Step 4: Use standard library iteration — don't reinvent `next()`, `zip()`, `enumerate()`
```python
shelf = BookShelf()
shelf.add("Refactoring")
shelf.add("Clean Code")
for i, book in enumerate(shelf): # enumerate works — shelf is iterable
print(f"{i+1}. {book}")
paired = list(zip(shelf, shelf)) # multiple iterators — independent
```
### Step 5: For lazy or infinite sequences, use generators
```python
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
for n in fibonacci(): # infinite iterator
if n > 100:
break
print(n)
```
## When NOT to Use
- **Random access collections where index-based access is the primary use** — if callers need `collection[i]` more than sequential traversal, a list interface is clearer.
- **When only one traversal order exists and the collection is simple** — Python lists already have full iterator support; wrapping them adds nothing.
## Common Mistakes
**Modifying the collection during iteration.** Deleting or inserting elements while iterating over them produces undefined behavior in most languages. Collect items to remove, then remove after the loop.
**Making the collection and iterator the same object.** If `BookShelf.__iter__` returns `self` and `BookShelf.__next__` manages position, two `for` loops share state and conflict. Return a new iterator object.
**Forgetting `StopIteration` in `__next__`.** An iterator that never raises `StopIteration` produces an infinite loop in a `for` loop. Always raise it when exhausted.Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!