Iterators
An iterator is any object that implements the iterator protocol: __iter__() and __next__().
Iterable vs Iterator
__iter__ |
__next__ |
|
|---|---|---|
| Iterable | Yes (returns an iterator) | No |
| Iterator | Yes (returns self) |
Yes |
# A list is iterable, not an iterator
lst = [1, 2, 3]
it = iter(lst) # create an iterator from the iterable
next(it) # 1
next(it) # 2
next(it) # 3
next(it) # raises StopIteration
How for Works Internally
for item in iterable:
...
# is equivalent to:
it = iter(iterable)
while True:
try:
item = next(it)
except StopIteration:
break
Custom Iterator
class CountUp:
def __init__(self, start, stop):
self.current = start
self.stop = stop
def __iter__(self):
return self # the object IS its own iterator
def __next__(self):
if self.current >= self.stop:
raise StopIteration
value = self.current
self.current += 1
return value
for n in CountUp(1, 4):
print(n) # 1 2 3
Useful Built-ins That Return Iterators
range(5) # range object (lazy)
enumerate(lst) # yields (index, value) pairs
zip(lst1, lst2) # yields tuples from multiple iterables
map(fn, lst) # applies fn lazily
filter(fn, lst) # filters lazily
reversed(lst) # iterates in reverse
itertools Module
import itertools
itertools.chain([1, 2], [3, 4]) # 1 2 3 4
itertools.islice(range(100), 5) # 0 1 2 3 4 (first 5)
itertools.cycle([1, 2, 3]) # 1 2 3 1 2 3 ... (infinite)
itertools.count(10, 2) # 10 12 14 ... (infinite)
itertools.product("AB", repeat=2) # AA AB BA BB
itertools.combinations([1,2,3], 2) # (1,2) (1,3) (2,3)
itertools.permutations([1,2,3], 2) # (1,2) (1,3) (2,1) ...
Key Interview Points
- An iterator is one-pass: once exhausted, it cannot be restarted. Create a new one.
- An iterable (list, tuple, str) can produce fresh iterators repeatedly via
iter(). forloops calliter()automatically.map()andfilter()return iterators in Python 3 (lazy), unlike Python 2 (lists).- Making a class both iterable and its own iterator is fine for single-use sequences; for reusable iterables, return a separate iterator object from
__iter__.