Python's next() function

Trey Hunner smiling in a t-shirt against a yellow wall
Trey Hunner
5 min. read Python 3.8—3.12
Share
Copied to clipboard.
Tags

Let's talk about one of the more advanced built-in functions in Python: the next function.

Getting the next line from a file

You can use Python's next function to get the next line from a file:

>>> with open("poems/harlem.txt", mode="rt") as file:
...     first_line = next(file)
...
>>> first_line
'What happens to a dream deferred?\n'

The next function doesn't just work on file objects though. For example, it also works on csv.reader objects:

>>> import csv
>>> with open("penguins_small.csv", mode="rt", newline="") as penguins_file:
...     reader = csv.reader(penguins_file)
...     headers = next(reader)
...
>>> headers
['species', 'island', 'bill_length_mm', 'bill_depth_mm', 'flipper_length_mm', 'body_mass_g', 'sex']

We just used next to pop off the header row just before we start reading a CSV file.

Besides file objects and csv.reader objects, what else does the next function work with?

You can't use next on most iterables

If you pass a list to the next function, Python will raise a TypeError

>>> numbers = [2, 1, 3, 4, 7]
>>> next(numbers)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'list' object is not an iterator

The next function doesn't work on lists.

It also doesn't work on dictionaries, sets, strings, tuples, or most other iterables.

What type of object does the next function work with?

Iterators work with next

Python's next function is designed to be used with iterators.

You can think of an iterator in two ways:

  1. Iterators are iterables that perform work as you loop over them (they're lazy) and are consumed as you loop over them
  2. Iterators are the objects that power all iterables in Python

Files are iterators, but so are generators. Here we're using a generator expression to make a generator object and then using next on that generator:

with open("article.md") as file:
    first_non_header = next(
        line
        for line in file
        if line.strip() and not line.startswith("#")
    )

Most of Python's looping helpers also return iterators. For example the return value from the enumerate and reversed built-in functions are iterators. So we could get the last item in a reversible iterable (such as a dictionary), like this:

>>> color_counts = {"blue": 2, "green": 1, "orange": 5, "purple": 3}
>>> last_key = next(reversed(color_counts))
>>> last_key
'purple'

This includes pretty much all the looping helpers in Python's itertools module:

>>> from itertools import count
>>> c = count()
>>> next(c)
0
>>> next(c)
1

So the next function works with files, generators, and any other iterator. But what if we wanted to get the first item from an iterable that isn't an iterator, like a dictionary? Is that possible with next?

The answer is yes, but with an extra step.

Using the next function with any iterable

Let's say we have a dictionary and we'd like to get the first item from it:

>>> color_counts = {"blue": 2, "green": 1, "orange": 5, "purple": 3}

Dictionaries are iterables. You can get an iterator from any iterable in Python by passing it to the built-in iter function.

>>> color_iterator = iter(color_counts)

And iterators work with the next function:

>>> first_key = next(color_iterator)
>>> first_key
'blue'

Since iterators are also iterables, they even work with the built-in iter function:

>>> iterator_of_iterator = iter(color_iterator)

Which means we can use next to get the first item from any iterable as long as we pass that iterable to the built-in iter function first:

first_item = next(iter(any_iterable))

For more on the relationship between iterables and iterators, see the iterator protocol.

The default value

If next is given an empty iterator, it will raise a StopIteration exception:

>>> color_counts = {}
>>> next(iter(color_counts))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

To suppress that StopIteration exception, you can supply a default value to the next function.

>>> next(iter(color_counts), 0)
0

Whenever next is used with a default value, that value will be returned whenever the given iterator is empty.

Prefer for loops over repeated next calls

If you need to get the next item from an iterable repeatedly, using a for loop is probably preferable to manually calling next over and over.

numbers = [2, 1, 3, 4, 7]

for number in numbers:
    print(number)

If we were to implement that same for loop using iter and next calls, our code would be much less readable:

number_iterator = iter(numbers)
while True:
    try:
        number = next(number_iterator)
        print(number)
    except StopIteration:
        break

Python's for loops are iterator-powered and they do low-level looping work for us.

When to use the next function

Python's for loops are powerful and they're usually preferable to next. So when is next used?

The most common use case you'll likely have for next is to pop off just one value from a generator, file, or other iterator.

import csv

with open("penguins_small.csv", mode="rt", newline="") as penguins_file:
    reader = csv.reader(penguins_file)
    headers = next(reader)

Sometimes you'll just need that value on its own. But sometimes you'll want to skip over the first value before looping over the rest of your items:

import csv

with open("penguins_small.csv", mode="rt", newline="") as penguins_file:
    reader = csv.reader(penguins_file)
    headers = next(reader)  # We're popping the header row off first
    for row in reader:
        ...  # Then we'll process non-header rows in some way

There are other uses of the next function, but they're lower-level and more advanced.

Looping helpers often use next

What are those more advanced uses of next? Well, the next function is often used to implement looping helpers.

When a generator function or another iterator object wants to augment the way for loops work, it might be necessary to manually call iter and next. For example the implementation of the built-in zip function (which allows for looping over multiple iterables at the same time in Python) looks pretty much like this:

def zip(*iterables):
    iterators = [iter(it) for it in iterables]
    sentinel = object()
    while iterators:
        result = []
        for it in iterators:
            elem = next(it, sentinel)
            if elem is sentinel:
                return
            result.append(elem)
        yield tuple(result)

That function manually calls next on iterators corresponding to each of the given iterables, one after the other.

If you're curious about that sentinel = object() line, see Unique sentinel values in Python.

Use next for consuming a single iterator item

Python's next function is typically used to consume a single item from an iterator. That iterator might be a file object (we're skipping the first two lines here):

with open("poems/harlem.txt", mode="rt") as file:
    next(file)  # Skip first line
    next(file)  # Skip second line
    for line in file:
        print(line.strip())

Or that iterator might be a generator or a looping helper:

>>> from pathlib import Path
>>> readme_path = next(
...     path
...     for path in Path().rglob("*.md")
...     if path.stem.lower() in ('readme', 'read me')
... )
>>> readme_path
PosixPath('README.md')

Here we've used next on a generator object to find the first markdown file named readme or read me while ignoring the capitalization of the filename.

If you find the next function confusing, you can always use a for loop and a break statement instead:

my_iterable = [2, 1, 3, 4, 7, 11]

# Get the first element of the iterable
first_item = None
for item in my_iterable:
    first_item = item
    break

print(first_item)

For more on iterators, see iterators in Python and Python's Iterator Protocol.

A Python Tip Every Week

Need to fill-in gaps in your Python skills? I send weekly emails designed to do just that.