Iterating over iterables with next() gives you fine-grained, manual control over stepping through an iterable one element at a time — in contrast to the automatic, high-level for loop. The next() function retrieves the next item from an iterator, and when there are no more items, it raises a StopIteration exception (which is how for loops know when to stop). This low-level approach is powerful for custom iteration logic, generators, coroutines, and understanding Python’s iterator protocol deeply.
In 2026, next() is still essential — especially when building generators, custom iterators, or handling streams of data (files, APIs, infinite sequences). Here’s a complete, practical guide to iterating with next(): how it works, real patterns, error handling, and when to prefer it over for loops.
Every iterable can be turned into an iterator using iter(). The iterator object has a __next__() method (called by next()) that returns the next value — until it runs out and raises StopIteration.
fruits = ["apple", "banana", "cherry"]
# Create an iterator from the iterable
fruit_iter = iter(fruits)
# Manually step through with next()
print(next(fruit_iter)) # apple
print(next(fruit_iter)) # banana
print(next(fruit_iter)) # cherry
# No more items ? StopIteration
try:
print(next(fruit_iter))
except StopIteration:
print("No more fruits!")
Real-world pattern: reading lines from a file one at a time — next() lets you peek at the first line or handle headers without loading everything.
with open("data.csv", "r") as f:
line_iter = iter(f) # File is iterable ? iterator over lines
# Peek at header
header = next(line_iter).strip()
print(f"Header: {header}")
# Process remaining lines
total = 0
for line in line_iter: # Continue with for loop
try:
value = float(line.strip())
total += value
except ValueError:
print(f"Skipping invalid line: {line.strip()}")
print(f"Total: {total}")
Another powerful use: working with generators — functions that yield values lazily. next() pulls values on demand, perfect for infinite sequences or memory-efficient processing.
def infinite_counter(start: int = 0):
"""Generator that yields numbers forever."""
count = start
while True:
yield count
count += 1
counter = infinite_counter(100)
print(next(counter)) # 100
print(next(counter)) # 101
print(next(counter)) # 102
# Keep calling next() as needed — never loads all values
Best practices make next() iteration safe and readable. Always wrap next() in try/except StopIteration — it’s the only way to know the end without crashing. Use next(iterable, default) — the second argument provides a fallback if the iterable is empty (no exception raised). Prefer for loops for simple, full iteration — they handle StopIteration automatically. Reserve next() for cases where you need manual control: peeking at the first item, skipping headers, stepping conditionally, or working with infinite generators. Modern tip: combine next() with itertools.islice() for slicing generators, or itertools.takewhile() for conditional iteration. In production, log or handle empty iterables gracefully — don’t assume there’s always data.
Iterating with next() gives you low-level control over iteration — essential for generators, custom protocols, and memory-efficient streams. In 2026, use it with try/except StopIteration, default fallbacks, and clear intent. Combine it with for loops when full traversal is needed — you get the best of both worlds: precision and simplicity.
Next time you need to peek, skip, or step through data manually — reach for iter() and next(). It’s Python’s clean way to take iteration into your own hands.