Python’s range() Function (Guide)

Python’s built-in range function is a handy tool to know you need to perform an action a specific number of times.

By the end of this article, you’ll:

  • Understand how Python’s range function works
  • Know how the implementations differ in Python 2 and Python 3
  • Have seen a number of hands-on range() examples
  • Be equipped to work around some of its limitations

Let’s get cracking!

The History of range()

Although range() in Python 2 and range() in Python 3 may share a name, they are entirely different animals. In fact, range() in Python 3 is just a renamed version of a function that is called xrange in Python 2.

Originally, both range() and xrange() produced numbers that could be iterated over with for-loops, but the former generated a list of those numbers all at once while the latter produced numbers lazily, meaning numbers were returned one at a time as they were needed.

Having huge lists hang around takes up memory, so it’s no surprise that xrange() replaced range(), name and all. You can read more about this decision and the xrange() vs range() background in PEP 3100.

For the rest of this article, you’ll be using the function as it exists in Python 3.

Here we go!

Let’s Loop

Before we dive into seeing how range() works, we need to take a look at how looping works. Looping is a key computer science concept. If you want to be a good programmer, mastering loops is among the first steps you need to take.

Here’s an example of a for-loop in Python:

captains = ['Janeway', 'Picard', 'Sisko']

for captain in captains:
    print(captain)

The output looks like this:

Janeway
Picard
Sisko

As you can see, a for-loop enables you to execute a specific block of code however many times you want. In this case, we looped through a list of captains and printed each of their names.

Although Star Trek is great and everything, you may want to do more than simply loop through a list of captains. Sometimes, you just want to execute a block of code a specific number of times. Loops can help you do that!

Try the following code with numbers that are divisible by three:

numbers_divisible_by_three = [3, 6, 9, 12, 15]

for num in numbers_divisible_by_three:
    quotient = num / 3
    print("{} divided by 3 is {}.".format(num, int(quotient)))

The output of that loop will look like this:

3 divided by 3 is 1.
6 divided by 3 is 2.
9 divided by 3 is 3.
12 divided by 3 is 4.
15 divided by 3 is 5.

That’s the output we wanted, so the loop got the job done adequately, but there is another way to get the same result by using range().

Now that you’re more familiar with loops, let’s see how you can use range() to simplify your life.

Getting Started With range()

So how does Python’s range function work? In simple terms, range() allows you to generate a series of numbers within a given range. Depending on how many arguments you pass to the function, you can decide where that series of numbers will begin and end as well as how big the difference will be between one number and the next.

Here’s a sneak peek of range() in action:

for i in range(3, 16, 3):
    quotient = i / 3
    print("{} divided by 3 is {}.".format(i, int(quotient)))

In this for-loop, you were able to simply create a range of numbers that are divisible by 3, so you didn’t have to provide each of them yourself.

There are three ways you can call range():

  1. range(stop) takes one argument.
  2. range(start, stop) takes two arguments.
  3. range(start, stop, step) takes three arguments.

range(stop)

When you call range() with one argument, you will get a series of numbers that starts at 0 and includes every whole number up to, but not including, the number you have provided as the stop.

Here’s what that looks like in practice:

for i in range(3):
    print(i)

The output of your loop will look like this:

0
1
2

That checks out: we have all the whole numbers from 0 up to but not including 3, the number you provided as the stop.

range(start, stop)

When you call range() with two arguments, you get to decide not only where the series of numbers stops but also where it starts, so you don’t have to start at 0 all the time. You can use range() to generate a series of numbers from A to B using a range(A, B). Let’s find out how to generate a range starting at 1.

Try calling range() with two arguments:

for i in range(1, 8):
    print(i)

Your output will look like this:

1
2
3
4
5
6
7

So far, so good: you have all the whole numbers from 1 (the number you provided as the start) up to but not including 7 (the number you provided as the stop).

But if you add one more argument, then you’ll be able to reproduce the output you got earlier when you were using the list named numbers_divisible_by_three.

range(start, stop, step)

When you call range() with three arguments, you can choose not only where the series of numbers will start and stop but also how big the difference will be between one number and the next. If you don’t provide a step, then range() will automatically behave as if the step is 1.

Now that you know how to use step, you can finally revisit that loop we saw earlier with division by 3.

Try it for yourself:

for i in range(3, 16, 3):
    quotient = i / 3
    print("{} divided by 3 is {}.".format(i, int(quotient)))

Your output will look exactly like the output of the for-loop you saw earlier in this article, when you were using the list named numbers_divisible_by_three:

3 divided by 3 is 1.
6 divided by 3 is 2.
9 divided by 3 is 3.
12 divided by 3 is 4.
15 divided by 3 is 5.

As you see in this example, you can use the step argument to increase towards a higher number. That’s called incrementing.

Incrementing With range()

If you want to increment, then you need step to be a positive number. To get an idea of what this means in practice, type in the following code:

for i in range(3, 100, 25):
    print(i)

If your step is 25, then the output of your loop will look like this:

3
28
53
78

You got a range of numbers that were each greater than the preceding number by 25, the step you provided.

Now that you’ve seen how you can step forwards through a range, it’s time to see how you can step backwards.

Decrementing With range()

If your step is positive, then you move through a series of increasing numbers and are incrementing. If your step is negative, then you move through a series of decreasing numbers and are decrementing. This allows you to go through the numbers backwards.

In the following example, your step is -2. That means that you’ll be decrementing by 2 for each loop:

for i in range(10, -6, -2):
    print(i)

The output of your decrementing loop will look like this:

10
8
6
4
2
0
-2
-4

You got a range of numbers that were each smaller than the preceding number by 2, the absolute value of the step you provided.

The most Pythonic way to create a range that decrements is to use range(start, stop, step). But Python does have a built-in reversed function. If you wrap range() inside reversed(), then you can print the integers in reverse order.

Give this a try:

for i in reversed(range(5)):
    print(i)

You’ll get this:

4
3
2
1
0

range() makes it possible to iterate over a decrementing sequence of numbers, whereas reversed() is generally used to loop over a sequence in reverse order.

Going Deeper With range()

Now that you know the basics of how to use range(), it’s time to dig a little deeper.

range() is mainly used for two purposes:

  1. Executing the body of a for-loop a specific number of times
  2. Creating more efficient iterables of integers than can be done using lists or tuples

The first use is probably the most common, and you could make the case that itertools gives you a more efficient way to construct iterables than range() does.

Here are a few more points to keep in mind when you use range.

range() is a type in Python:

>>>

>>> type(range(3))
<class 'range'>

You can access items in a range() by index, just as you would with a list:

>>>

>>> range(3)[1]
1
>>> range(3)[2]
2

You can even use slicing notation on a range(), but the output in a REPL may seem a little strange at first:

>>>

>>> range(6)[2:5]
range(2, 5)

Although that output may look odd, slicing a range() just returns another range().

The fact that you can access elements of a range() by index and slice a range() highlights an important fact: range() is lazy, unlike a list, but isn’t an iterator.

Floats and range()

You may have noticed that all of the numbers we have been dealing with so far have been whole numbers, which are also called integers. That’s because range() can take only integers as arguments.

A Word on Floats

In Python, if a number is not a whole number, then it is a float. There are some differences between integers and floats.

An integer (int data type):

  • Is a whole number
  • Does not include a decimal point
  • Can be positive, negative, or 0

A floating point number (float data type):

  • Can be any number that includes a decimal point
  • Can be positive or negative

Try calling range() with a float and see what happens:

for i in range(3.3):
    print(i)

You should get the following error message:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'float' object cannot be interpreted as an integer

If you need to find a workaround that will allow you to use floats, then you can use NumPy.

Using NumPy

NumPy is a third-party Python library. If you are going to use NumPy, your first step is to check if you have it installed.

Here’s how you can do that in your REPL:

>>>

>>> import numpy

If you get a ModuleNotFoundError, then you need to install it. To do so, go to your command line and enter pip install numpy.

Once you have it installed, put in the following:

import numpy as np

np.arange(0.3, 1.6, 0.3)

It will return this:

array([0.3, 0.6, 0.9, 1.2, 1.5])

If you want to print each number on its own line, you can do the following:

import numpy as np

for i in np.arange(0.3, 1.6, 0.3):
    print(i)

This is the output:

0.3
0.6
0.8999999999999999
1.2
1.5

Where did 0.8999999999999999 come from?

Computers have trouble saving decimal floating-point numbers in binary floating-point numbers. This leads to all sorts of unexpected representations of numbers.

Whether or not these floating point errors are an issue for you depends on the problem you’re solving. The errors are going to be in something like the 16th decimal place, which is insignificant most of the time. They are so small that, unless you’re working on calculating satellite orbital trajectories or something, you don’t need to worry about it.

Alternatively, you could also use np.linspace(). It does essentially the same thing but uses different parameters. With np.linspace(), you specify start and end (both inclusive) as well as the length of the array (instead of step).

For instance, np.linspace(1, 4, 20) gives 20 equally spaced numbers: 1.0, ..., 4.0. On the other hand, np.linspace(0, 0.5, 51) gives 0.00, 0.01, 0.02, 0.03, ..., 0.49, 0.50.

Go Forth and Loop

You now understand how to use range() and work around its limitations. You also have an idea of how this important function has evolved between Python 2 and Python 3.

The next time you need to perform an action a specific number of times, you’ll be all set to loop your heart out!

Happy Pythoning!


[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short & sweet Python Trick delivered to your inbox every couple of days. >> Click here to learn more and see examples ]