Back to Blog
Python

Python itertools count: Infinite Counting Made Simple

Learn how to use itertools.count for infinite sequences in Python, including start, step, and practical patterns with code examples.

itertoolsgeneratorsinfinite sequencescountingiteration
Illustration of Python itertools.count generating an infinite sequence of numbers

The itertools.count function returns an iterator that produces evenly spaced numbers indefinitely. It is useful whenever you need an automatic counter or an endless numeric series without managing a loop variable yourself. This article covers its syntax, common patterns, and the limits you need to keep in mind.

What itertools.count Does

The count function lives in the itertools module. Its signature is itertools.count(start=0, step=1). It returns an iterator that yields evenly spaced values starting at start and increasing by step on each call. The step can be positive, negative, or zero, and it is commonly an integer or float.

from itertools import count counter = count(10, 2) print(next(counter)) # 10 print(next(counter)) # 12 print(next(counter)) # 14

The iterator never raises StopIteration on its own. It is designed to be used with tools that limit how many values you consume, such as islice or takewhile.

Basic Usage: Creating an Infinite Counter

The simplest use is a bare count() which starts at 0 and increments by 1. This is often used as an index generator when you need to pair elements with their position.

from itertools import count for i, value in zip(count(), ['a', 'b', 'c']): print(i, value) # 0 a # 1 b # 2 c

Notice that count() works like a built-in enumerate but with more flexibility: you can start at a different offset or use a non-unit step.

Using count with zip and Other Itertools

count pairs naturally with zip to generate sequential labels or time-like values. For example, you can create a list of timestamped events without storing a counter variable:

from itertools import count events = ['login', 'logout', 'purchase'] for timestamp, event in zip(count(1000, 5), events): print(f"{timestamp}: {event}") # 1000: login # 1005: logout # 1010: purchase

It also combines with islice to take a finite number of values from the infinite sequence:

from itertools import count, islice first_five = list(islice(count(10, 2), 5)) print(first_five) # [10, 12, 14, 16, 18]

You can use takewhile to stop based on a condition:

from itertools import count, takewhile under_20 = list(takewhile(lambda x: x < 20, count(10, 2))) print(under_20) # [10, 12, 14, 16, 18]

Stopping Infinite Loops: islice and takewhile

Because count never ends, you must always pair it with a limiting construct unless you deliberately want an infinite loop. The two most common are itertools.islice and itertools.takewhile.

islice takes a number of items or a slice range. It is the direct equivalent of slicing a list but for iterators.

from itertools import count, islice # First 10 even numbers starting at 0 first_ten_even = list(islice(count(0, 2), 10))

takewhile stops when a predicate becomes false. This is useful when you want to generate numbers up to a certain threshold.

from itertools import count, takewhile # Squares of numbers up to 100 squares = list(takewhile(lambda x: x <= 100, (i**2 for i in count())))

Practical Patterns: Numbering, IDs, and Time-Based Counting

A common pattern is using count to assign unique IDs to objects in a stream. Since count is lazy, you can create an ID generator without storing a list of all IDs.

from itertools import count def make_id_generator(): return count(1) id_gen = make_id_generator() print(next(id_gen)) # 1 print(next(id_gen)) # 2

Another pattern is generating a sequence of time intervals. For example, if you want to poll a service every 5 seconds, you can use count(0, 5) to produce the elapsed seconds.

from itertools import count for elapsed in count(0, 5): if elapsed >= 30: break print(f"Polling at t={elapsed}s")

This avoids maintaining a separate elapsed variable that you manually increment.

Performance and Memory Considerations

count is an iterator, so it does not precompute or store the entire sequence. Each call to next() computes the next value on the fly. This makes it memory efficient even for very large or infinite sequences. The overhead per iteration is minimal: a single addition and, if you use takewhile, a comparison.

However, be aware that count stores its current value internally. If you create many count iterators, each holds its own state. That is usually fine.

Another subtle point: when start and step are integers, count uses Python's arbitrary-precision integers, so there is no overflow risk. For float steps, floating-point rounding can cause values to drift over many iterations. If you need exact decimal arithmetic, use Decimal or fractions.Fraction as the start and step values.

Common Mistakes and Edge Cases

One common mistake is forgetting to limit the sequence. If you iterate over count() directly with a for loop without a break condition, you get an infinite loop. Always pair it with islice, takewhile, or an explicit break.

Another edge case is using a negative step. count(10, -2) produces 10, 8, 6, and so on. This works, but takewhile conditions must be written accordingly. For example, takewhile(lambda x: x > 0, count(10, -2)) yields 10, 8, 6, 4, 2, then stops.

Also note that count is not resettable. Once you create an iterator, you cannot rewind it. If you need to start over, create a new count object.

Finally, using count with zip on short iterables is safe because zip stops when the shortest input ends. But if you use zip_longest with count, the result will be infinite unless you also limit it.

When to Use a Generator Instead

Sometimes a simple generator function is clearer than count, especially when the increment logic is not a simple arithmetic progression. For example, if you need to skip certain values or apply a transformation, a generator is more readable.

def custom_counter(): n = 0 while True: yield n n += 2

This is functionally similar to count(0, 2), but the generator makes the step explicit and allows more complex logic. Use count when the arithmetic progression is exactly what you need and you want to avoid boilerplate.

The decision often comes down to whether the sequence is a pure arithmetic series. If yes, count is concise and efficient. If you need to modify the value or the step based on external state, a generator gives you more control.

Python itertools.count: Practical Usage and Code Examples | RYUSLOG DEV