Card 03/ 09
Iterator Template in Python, Java and TypeScript
__iter__ and __next__, Iterable and Iterator, and Symbol.iterator, for one small collection.
The Iterator template is the pattern in its pure form, with the participant names from the class diagram, written against each language's own iterator protocol so that the language's loop can use it. A ConcreteAggregate holds three items, and a ConcreteIterator returns them one at a time. The steps build the Python version, and the tabs give the complete program in three languages, each with its output.
Building the template one part at a time
Aggregate
Returns a fresh iterator each time it is asked.
def __iter__(self) -> "ConcreteIterator":
return ConcreteIterator(self._items)Position
Kept in the iterator, starting at zero.
self._position = 0Next element
Returns the element and moves on, or signals the end.
def __next__(self) -> str:
if self._position >= len(self._items):
raise StopIteration
item = self._items[self._position]
self._position += 1
return itemThe complete template
from collections.abc import Iterator
class ConcreteAggregate:
def __init__(self, *items: str) -> None:
self._items = list(items)
def __iter__(self) -> "ConcreteIterator":
return ConcreteIterator(self._items)
class ConcreteIterator(Iterator[str]):
def __init__(self, items: list[str]) -> None:
self._items = items
self._position = 0
def __next__(self) -> str:
if self._position >= len(self._items):
raise StopIteration
item = self._items[self._position]
self._position += 1
return item
aggregate = ConcreteAggregate("a", "b", "c")
for item in aggregate:
print("item:", item)
print("again:", " ".join(aggregate))item: a
item: b
item: c
again: a b cimport java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
public class ItTemplate {
public static void main(String[] args) {
ConcreteAggregate aggregate = new ConcreteAggregate(List.of("a", "b", "c"));
for (String item : aggregate) {
System.out.println("item: " + item);
}
Iterator<String> again = aggregate.iterator();
System.out.print("again:");
while (again.hasNext()) {
System.out.print(" " + again.next());
}
System.out.println();
}
}
class ConcreteAggregate implements Iterable<String> {
private final List<String> items;
ConcreteAggregate(List<String> items) {
this.items = items;
}
public Iterator<String> iterator() {
return new ConcreteIterator(items);
}
}
class ConcreteIterator implements Iterator<String> {
private final List<String> items;
private int position = 0;
ConcreteIterator(List<String> items) {
this.items = items;
}
public boolean hasNext() {
return position < items.size();
}
public String next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return items.get(position++);
}
}item: a
item: b
item: c
again: a b cclass ConcreteAggregate implements Iterable<string> {
private readonly items: string[];
constructor(...items: string[]) {
this.items = items;
}
[Symbol.iterator](): Iterator<string> {
return new ConcreteIterator(this.items);
}
}
class ConcreteIterator implements Iterator<string> {
private position = 0;
constructor(private readonly items: string[]) {}
next(): IteratorResult<string> {
if (this.position >= this.items.length) {
return { done: true, value: undefined };
}
return { done: false, value: this.items[this.position++] };
}
}
const aggregate = new ConcreteAggregate("a", "b", "c");
for (const item of aggregate) {
console.log("item:", item);
}
console.log("again:", [...aggregate].join(" "));item: a
item: b
item: c
again: a b cAll three print the items, then print them again from a second walk. The second walk starts at the beginning because the aggregate creates a new iterator for it, with its own position at zero.
The three protocols say the end in different ways. Python's __next__ raises StopIteration, Java's hasNext returns false, and TypeScript's next returns an object with done set to true. In each, the language's loop calls these methods for you, which is why for works on any class that follows the protocol. The Python ConcreteIterator inherits from collections.abc.Iterator, which supplies the __iter__ an iterator must also have.
← → move through sections and cards