Desk Clarity

Everyday document & browser help

Python generator after next(): loop over the remaining items safely

By ON Lab · Sources checked November 8, 2026

Calling next(iterator) consumes one item. A subsequent for loop on that same iterator visits the remaining items. If a finite generator had only one item, the loop body runs zero times and execution continues afterward. Exhaustion does not restart that generator or by itself create an infinite loop.

A historical reader had already handled the first generated value and wanted later API work to begin at the second. They asked whether looping over the exhausted one-item generator could raise an error or run forever. We tested finite local generators without any API calls and checked the documented iterator protocol. The guarantee needs an important limit: a producer can still block, raise another exception or deliberately yield forever.

Keep and reuse the same iterator

def values():
    yield 0
    yield 1
    yield 2

iterator = values()
first = next(iterator)
remaining = list(iterator)
assert first == 0
assert remaining == [1, 2]
assert list(iterator) == []

Calling values creates the generator iterator. Calling next runs it until the next yield. The later list call consumes what remains, and the final list call demonstrates exhaustion. A for loop follows the same iterator protocol; the list calls are only an inspectable way to show the results in a tiny fixture.

Do not put a fresh call to values in the second loop if you intend to resume the first iterator. A new generator starts a separate sequence and can repeat the first item or repeat work performed by the generator body. Keep an explicit iterator variable to make that lifetime visible.

The one-item case exits normally

def single():
    yield 0

iterator = single()
first = next(iterator)
seen = []
for item in iterator:
    seen.append(item)
assert first == 0
assert seen == []

When the loop requests another item, this generator has finished. The iterator signals StopIteration, which a normal for loop uses to end iteration. It does not run the body again with the previous value. Code after the loop still executes unless some other control flow or exception intervenes.

The iterator protocol requires an exhausted iterator to stay exhausted. That explains the repeated empty result from our finite fixture. It is not a guarantee that every custom object somebody calls a generator correctly implements the protocol.

Handle the empty case before the loop

_MISSING = object()
iterator = iter([])
first = next(iterator, _MISSING)
if first is _MISSING:
    result = "no items"
else:
    result = (first, list(iterator))
assert result == "no items"

A bare next call on an empty iterator raises StopIteration before the later for loop is reached. The optional default lets you handle that case explicitly. A private sentinel distinguishes “no item” from legitimate values such as None, 0, False or an empty string. Choose an application-specific response to an empty input rather than letting it accidentally resemble the first result.

Our comparison covered empty, one-item and three-item input. The first values were missing, 0 and 0; the remaining lists were empty, empty and [1, 2]. All were exhausted after the remaining items had been consumed.

Separate iteration safety from request safety

If the first item's API result is already saved, only the remaining loop should issue later requests. In a test, substitute a local recorder for the request function and check which item values it receives. Do not rerun real requests merely to discover whether your iterator resets.

Skipping the first item by calling next still executes the generator's code needed to produce that item. If the generator itself makes requests, reading or skipping a value may already perform external work. The loop structure cannot prove that the generator body has no side effects.

Likewise, an infinite generator will keep yielding, and a generator waiting on a network response may block while asked for the next value. Those are properties of the producer, not consequences of iterating an already exhausted finite generator. Use the application's explicit request limits, timeouts and retry policy when external operations are involved. This local test does not validate those controls.

If later items disappear unexpectedly

Converting the entire generator to a list makes replay possible but also consumes it immediately and stores all items. That may be inappropriate for large or infinite sources. Recreating a generator can repeat computation and side effects. Choose a replay strategy only when the workload actually needs replay.

Completion checks

  1. Test zero, one and several generated values with a local recorder.
  2. Verify that the saved first item is not processed twice.
  3. Verify that every remaining intended item is processed once.
  4. Confirm what generator evaluation itself does before involving a real service.
  5. Keep bounded request and error handling separate from iterator exhaustion.

Checked November 8, 2026 with Python 3.9.6 finite generators. No real API, infinite producer, blocking network source or original application was tested. The reader question is historical and does not establish current search volume.

Sources