__next__

__next__(self)
Returns: any · Added in v3.9 · Updated March 18, 2026 · Keywords
iterator protocol dunder

__next__ is the method that makes an object an iterator. While __iter__ returns the iterator object itself, __next__ returns each successive item and raises StopIteration when exhausted.

Signature

def __next__(self):
    # Returns the next item in the sequence

Quick Example

class Counter:
    def __init__(self, limit):
        self.current = 0
        self.limit = limit
    
    def __iter__(self):
        return self
    
    def __next__(self):
        if self.current >= self.limit:
            raise StopIteration
        self.current += 1
        return self.current - 1

# Usage
for i in Counter(3):
    print(i)
# Output: 0
# Output: 1
# Output: 2

How It Works

The iteration protocol involves two methods:

  1. __iter__ — Returns the iterator object. Must return self for the object to be its own iterator.
  2. __next__ — Returns the next item. When no items remain, raise StopIteration.

Python’s for loops and next() built-in call __next__ automatically:

counter = Counter(3)
print(next(counter))  # Output: 0
print(next(counter))  # Output: 1
print(next(counter))  # Output: 2
print(next(counter))  # Raises StopIteration

Common Patterns

Stateful Iterators

Track internal state between calls:

class Fibonacci:
    def __init__(self, max_terms=10):
        self.a, self.b = 0, 1
        self.remaining = max_terms
    
    def __iter__(self):
        return self
    
    def __next__(self):
        if self.remaining <= 0:
            raise StopIteration
        self.remaining -= 1
        result = self.a
        self.a, self.b = self.b, self.a + self.b
        return result

# Usage
fib = Fibonacci(5)
print(list(fib))  # Output: [0, 1, 1, 2, 3]

One-Shot vs Reusable

Iterators are consumed after one pass. If you need to reuse, return a fresh iterator from __iter__:

class ReusableRange:
    def __init__(self, start, stop):
        self.start = start
        self.stop = stop
    
    def __iter__(self):
        return RangeIterator(self.start, self.stop)

class RangeIterator:
    def __init__(self, start, stop):
        self.current = start
        self.stop = stop
    
    def __next__(self):
        if self.current >= self.stop:
            raise StopIteration
        self.current += 1
        return self.current - 1

# Usage
r = ReusableRange(0, 3)
print(list(r))  # Output: [0, 1, 2]
print(list(r))  # Output: [0, 1, 2] — works again!

Gotchas

Forgetting to Raise StopIteration

Always raise StopIteration when iteration ends. Otherwise, for loops may behave unexpectedly:

# Wrong — may cause infinite loop in some contexts
def __next__(self):
    if self.index >= len(self.items):
        return None  # Don't do this
    return self.items[self.index]

PEP 479: StopIteration in Generators

Since Python 3.7, StopIteration raised inside a generator is converted to RuntimeError. This prevents silent infinite loops:

def bad_generator():
    value = 0
    while True:
        yield value
        value += 1
        if value > 3:
            raise StopIteration  # Becomes RuntimeError in Python 3.7+

Use return instead to exit generators:

def good_generator(limit):
    for i in range(limit):
        yield i

# Or use `return` directly:
def good_generator(limit):
    value = 0
    while value < limit:
        yield value
        value += 1
    return  # Clean exit

Modifying During Iteration

Modifying a collection while iterating over it can cause unexpected behavior. Use iterators to isolate the iteration state:

items = [1, 2, 3]
for item in items:  # Iterates over a snapshot
    if item == 2:
        items.append(4)  # Safe — doesn't affect current iteration

See Also