Skip to main content

How to Create a Dynamic Range for Loop in Python

Loops are fundamental in Python, and the for loop is one of the most frequently used constructs for iterating over sequences. In many real-world scenarios, however, you will not know the loop's boundaries ahead of time. The start, stop, or step values might come from user input, a database query, a configuration file, or a calculation performed at runtime.

This guide walks you through multiple methods to create a dynamic range for a for loop in Python, from the built-in range() function to generators, NumPy, and more. Each approach is explained with clear examples and outputs so you can choose the best fit for your use case.

Using range() with Variables

The most straightforward way to build a dynamic range is by passing variables to Python's built-in range() function. It accepts start, stop, and step parameters, all of which can be set at runtime:

start = 1
stop = 10
step = 2

for i in range(start, stop, step):
print(i, end=" ")

Output:

1 3 5 7 9

range(start, stop, step) generates integers from start (inclusive) to stop (exclusive), incremented by step. Because all three parameters are ordinary variables, you can reassign them at any point before the loop executes, making the range fully dynamic.

tip

range() only works with integers. If you need floating-point steps, see the Using numpy.arange() for Float Steps section below, or create a custom generator function.

Using a Generator Function

Generator functions give you full control over how values are produced. They yield one value at a time, which makes them memory-efficient for very large or complex sequences:

def dynamic_range(start, stop, step):
while start < stop:
yield start
start += step

for i in dynamic_range(1, 10, 2):
print(i, end=" ")

Output:

1 3 5 7 9

The yield keyword turns the function into a generator. Values are produced lazily, meaning they are generated only when the loop requests the next item. Unlike range(), a custom generator can handle floats, custom logic, or non-linear progressions without any external library.

Generator with a Float Step

One of the biggest advantages of a custom generator is native support for floating-point ranges:

def float_range(start, stop, step):
while start < stop:
yield round(start, 10) # round to avoid floating-point drift
start += step

for val in float_range(0.0, 1.0, 0.2):
print(val, end=" ")

Output:

0.0 0.2 0.4 0.6 0.8 

The round() call prevents tiny floating-point accumulation errors from producing values like 0.6000000000000001 instead of 0.6.

Using numpy.arange() for Float Steps

The NumPy library provides numpy.arange(), which natively supports floating-point start, stop, and step values. This is particularly useful in scientific computing and data analysis:

import numpy as np

start = 0.5
stop = 5.0
step = 0.5

for i in np.arange(start, stop, step):
print(i, end=" ")

Output:

0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 

numpy.arange() returns a NumPy array, so you can also use it outside of loops for vectorized operations.

caution

Due to floating-point precision, numpy.arange() may occasionally include or exclude the endpoint unexpectedly. For applications where the exact number of points matters more than the step size, consider using numpy.linspace() instead:

import numpy as np

# Generate exactly 5 evenly spaced points between 0 and 1 (inclusive)
for val in np.linspace(0, 1, 5):
print(val, end=" ")

Output:

0.0 0.25 0.5 0.75 1.0 

Using itertools.count() for Open-Ended Ranges

When you do not know in advance where the loop should stop, itertools.count() generates an infinite sequence of numbers. You must include a termination condition to prevent an endless loop:

from itertools import count

start = 3
step = 2

for i in count(start, step):
if i > 15:
break
print(i, end=" ")

Output:

3 5 7 9 11 13 15

count(start, step) produces values indefinitely, starting from start and incrementing by step. The break statement acts as the dynamic stop condition, which can be based on any runtime logic.

A Common Mistake: Forgetting the Termination Condition

from itertools import count

# This will run forever and may freeze your program!
for i in count(0, 1):
print(i)
danger

Forgetting the break condition with itertools.count() results in an infinite loop that may freeze your program or consume all available memory. Always guard infinite iterators with a break statement or use them inside constructs like itertools.takewhile().

A safer alternative uses takewhile() to define the stop condition declaratively:

from itertools import count, takewhile

for i in takewhile(lambda x: x <= 15, count(3, 2)):
print(i, end=" ")

Output:

3 5 7 9 11 13 15

Building a Dynamic Range from User Input

A common real-world use case is collecting the range parameters interactively from the user. The input() function captures strings, which you convert to integers before passing them to range():

start = int(input("Enter the start of the range: "))
stop = int(input("Enter the end of the range: "))
step = int(input("Enter the step size: "))

for i in range(start, stop, step):
print(i, end=" ")

Sample interaction:

Enter the start of the range: 2
Enter the end of the range: 12
Enter the step size: 3
2 5 8 11

Validating User Input

Always validate user input to prevent crashes. A step of 0, for example, raises a ValueError because range() cannot increment by zero:

try:
start = int(input("Enter the start: "))
stop = int(input("Enter the stop: "))
step = int(input("Enter the step: "))

if step == 0:
raise ValueError("Step size cannot be zero")

for i in range(start, stop, step):
print(i, end=" ")
except ValueError as e:
print(f"Invalid input: {e}")
tip

For negative ranges (counting downward), the step must be negative and the start must be greater than the stop:

for i in range(10, 0, -2):
print(i, end=" ")

Output:

10 8 6 4 2

Using a List as a Dynamic Range

When your iteration values do not follow a linear pattern, or they are computed, filtered, or fetched from an external source, a list or any iterable can serve as the dynamic range:

values = [10, 20, 30, 40]

for i in values:
print(i, end=" ")

Output:

10 20 30 40

Because lists are mutable, you can build or modify them at runtime:

import random

# Dynamically generate a list of 5 random values between 1 and 100
values = [random.randint(1, 100) for _ in range(5)]

for i in values:
print(i, end=" ")

Possible output:

94 12 87 9 34 

This is especially useful when the loop values come from a database query, an API response, or any other dynamic data source.

Method Comparison

MethodSupports FloatsMemory EfficientInfinite RangeExternal Dependency
range() with variablesNoYesNoNone
Generator functionYesYesYesNone
numpy.arange()YesNo (array in memory)NoNumPy
itertools.count()YesYesYesNone (stdlib)
User input + range()NoYesNoNone
List as rangeYesNo (list in memory)NoNone

Conclusion

Python offers several flexible ways to create a dynamic range for a for loop.

  • Use range() with variables for the simplest integer-based sequences.
  • Choose a generator function when you need maximum flexibility and memory efficiency, especially for custom or float-based sequences.
  • Reach for numpy.arange() in scientific applications requiring floating-point steps, and use itertools.count()
  • when the stop condition is determined at runtime rather than known in advance.
  • User input lets you define the range interactively, and lists work well for non-linear or pre-computed sequences from external sources.

Choose the method that best matches your requirements for data type, memory usage, and whether the range boundaries are known ahead of time.