← 输入框|python.land|刷新

Python range() Function: How-To Tutorial With Examples

The Python range() function gives you a sequence of whole numbers. You’ll mostly use it in a for-loop, to repeat something a set number of times or to count from one number to another. The classic example is for i in range(5):

for i in range(5):
    print(i)Code language: Python (python)

Output:

0
1
2
3
4Code language: Python (python)

Notice two things: counting starts at 0, and the 5 itself is not included. You can call range in three ways:

  • range(stop) counts from 0 up to, but not including, stop.
  • range(start, stop) counts from start up to, but not including, stop.
  • range(start, stop, step) does the same, but jumps step numbers at a time. A negative step counts backwards.

Here are all three, turned into lists so you can see every number at once:

print(list(range(5)))         # stop
print(list(range(2, 6)))      # start, stop
print(list(range(0, 10, 3)))  # start, stop, step

# Output:
# [0, 1, 2, 3, 4]
# [2, 3, 4, 5]
# [0, 3, 6, 9]Code language: Python (python)

That’s the short version. The rest of this article covers each form in more detail, how to get an inclusive range, how to count backwards, how to turn a range into a list, and why a range uses almost no memory, no matter how big it is.

What is the Python range function?

Python’s range acts as a built-in function, and is commonly used for looping a specific number of times in for-loops. Like many things in Python, it’s actually a Python type (or class), but when you use it in a loop, you can treat it like a built-in function that returns a sequence of numbers.

Range gives you a calculated sequence of numbers based on your input. These are the three ways to call it:

# With one integer argument it
# counts from 0 to stop
range(stop)

# With two integer arguments it
# counts from start to stop
range(start, stop)

# With three integer arguments it
# counts from start to stop,
# with a defined step size
range(start, stop, step)Code language: Python (python)

The arguments start, stop, and step are always integers. When you call range, it returns an object of class range. This range object is iterable, meaning you can loop (iterate) over its values. The numbers aren’t stored anywhere. Range calculates each one when it’s needed, based on the start, stop, and step values. I’ll explain how that works further down.

for i in range(): using range in a for-loop

The line for i in range(n): is probably the most common way range shows up in Python code. Read it as “for every number i in the range 0 up to n, run the indented block.” On each pass through the loop, i gets the next number from the range:

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

# Output:
# 0
# 1
# 2Code language: Python (python)

There’s nothing special about the name i. It’s a regular variable, and i is just a tradition (short for index). You can name it number, day, or whatever describes the value.

Sometimes you don’t need the number at all, and only want to repeat something a few times. By convention, Python programmers name the variable _ in that case, to tell readers the value is ignored:

for _ in range(3):
    print("Hello!")

# Output:
# Hello!
# Hello!
# Hello!Code language: Python (python)

A loop over range(n) always runs exactly n times. That’s an easy way to remember what range(n) does, and it’s the reason the stop value is excluded. If you’d like to practice this in your browser, the lesson Count with range() in my Python Fundamentals course has exercises that check your code as you go.

How to use Python range

Let’s look at all three ways to use range in detail, starting with the simplest one.

Range with only a stop value

If you call range with one argument, that argument is the stop value. In this case, range starts counting at 0 and keeps going until it reaches the stop value.

For example, if you call range(5), the range object returns the values 0, 1, 2, 3, and 4 when you iterate over it. So, important to note: range stops as soon as it reaches the stop value, and won’t return the stop value itself.

If that sounds confusing, a simple example will hopefully clarify it. Here’s the range call in a for-loop:

for i in range(5):
    print(i)Code language: Python (python)

Output:

0
1
2
3
4

Why does range not include the end value?

You might be wondering why range doesn’t include the end value. It all comes down to an important principle called zero-based indexing. Computers start counting at zero, while humans start counting at 1. When a computer needs to number 3 elements, it starts with element 0, then 1, and then 2. It’s the same with indexing. When accessing elements in a Python list, the first element is at position 0. You access it like this: my_list[0].

Because of this zero-based indexing, it’s easier if Python ranges start counting at zero too. A list with 3 elements has the indexes 0, 1, and 2, which is exactly what range(3) gives you. It’s also the same rule that list slicing follows: my_list[0:3] gives you the elements at index 0, 1, and 2, but not 3.

Range with a start and stop value

You don’t always want to start counting at zero, and that’s why you can also call range with both a start and a stop value. A call to range(2, 5) returns the values 2, 3, and 4. The start value is included, the stop value is not:

for i in range(2, 5):
    print(i)

# Output:
# 2
# 3
# 4Code language: Python (python)

A handy way to think about range(start, stop): it gives you stop - start numbers. So range(2, 5) has 3 numbers in it.

Range with a positive step size

Finally, range() takes an optional third argument called the step size. The step size defines how big each step is between the calculated numbers. It’s best to illustrate this:

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

# Output:
# 0 2 4Code language: Python (python)

Each step skips a number since the step size is two. Let’s increase the step size:

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

# Output:
# 0 10 20 30 40 50 60 70 80 90 100Code language: Python (python)

Because I set stop to 101, the value 100 is also included in this range. With a stop value of 100, the last number would have been 90.

The step can’t be zero. A step of 0 would never get anywhere, so Python raises a ValueError if you try.

Range with a negative step size

Ranges can count backward as well. If you set the step size to a negative number, the range counts down. When you count backward, make sure that the value for start is larger than the value for stop. Here’s an example:

for i in range(5, 0, -1):
    print(i, end=" ")

# Output:
# 5 4 3 2 1Code language: Python (python)

This starts at 5 and counts backward to 0. Because ranges don’t include the stop value, 0 is not included. If you want to include 0, you need to set stop to -1. For example, a countdown from 10 to 0:

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

# Output:
# 10 9 8 7 6 5 4 3 2 1 0Code language: Python (python)

A common mistake is to swap start and stop but forget the negative step. Python doesn’t give you an error in that case. You get an empty range, and your loop silently runs zero times:

print(list(range(5, 0)))

# Output:
# []Code language: Python (python)

If the -1 arithmetic feels error-prone, there’s another option. The built-in reversed() function walks through a range from back to front. You write the range the normal way, and reversed() takes care of the rest:

for i in reversed(range(5)):
    print(i, end=" ")

# Output:
# 4 3 2 1 0Code language: Python (python)

I find this easier to read when I simply want the numbers of range(5) in the opposite order.

Python inclusive range: including the stop value

Python has no built-in inclusive range. The stop value is always excluded. To include it, add one to the stop value:

start, stop = 1, 10
print(list(range(start, stop + 1)))

# Output:
# [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]Code language: Python (python)

With a step size, the stop value only shows up if the steps land on it exactly. Any stop value that’s past your last number but not past the next step will work. Adding 1 is the simplest choice, and when you count down, you subtract 1 instead:

# 0 up to and including 20, in steps of 5
print(list(range(0, 21, 5)))

# 10 down to and including 0, in steps of 2
print(list(range(10, -1, -2)))

# Output:
# [0, 5, 10, 15, 20]
# [10, 8, 6, 4, 2, 0]Code language: Python (python)

If you need inclusive ranges in many places, you can write a small function that does the + 1 for you. For most code, though, range(start, stop + 1) is clear enough, and every Python programmer recognizes it.

Convert range to list

If you print a range, you don’t see the numbers in it. You see the range object itself:

print(range(5))

# Output:
# range(0, 5)Code language: Python (python)

That’s because a range doesn’t store its numbers; it calculates them when asked. To get an actual Python list, pass the range to the list() function, which can convert any iterable object to a list, including ranges:

numbers = list(range(1, 6))
print(numbers)
print(list(range(10, 0, -2)))

# Output:
# [1, 2, 3, 4, 5]
# [10, 8, 6, 4, 2]Code language: Python (python)

You could build the same list with a for-loop or a list comprehension, but list(range(...)) is shorter and faster. Use a list comprehension when you want to change each number on the way, like [i * i for i in range(5)] for the squares of 0 to 4.

You don’t need a list just to loop over the numbers, though. A for-loop works directly on the range. Only convert when you really need a list, for example because you want to change, append to, or shuffle the numbers. A tuple works the same way: tuple(range(3)) gives you (0, 1, 2).

range() and len(): looping over indexes

You’ll often see range(len(...)) used to loop over the indexes of a list. Since len(fruits) is 3 here, range(len(fruits)) gives you 0, 1, and 2, which are exactly the valid indexes:

fruits = ["apple", "banana", "cherry"]
for i in range(len(fruits)):
    print(i, fruits[i])

# Output:
# 0 apple
# 1 banana
# 2 cherryCode language: Python (python)

This works, but it’s usually not the best way. If you only need the items, loop over the list directly with for fruit in fruits:. If you need both the index and the item, use the built-in enumerate() function. It hands you both at once, so you don’t have to look up fruits[i] yourself:

fruits = ["apple", "banana", "cherry"]
for i, fruit in enumerate(fruits):
    print(i, fruit)

# Output:
# 0 apple
# 1 banana
# 2 cherryCode language: Python (python)

The output is the same, but the code reads better and there’s no index to get wrong. enumerate() can also start counting at another number, which is nice for numbered lists meant for humans:

fruits = ["apple", "banana", "cherry"]
for number, fruit in enumerate(fruits, start=1):
    print(f"{number}. {fruit}")

# Output:
# 1. apple
# 2. banana
# 3. cherryCode language: Python (python)

There are still good reasons to use range(len(...)). One is when you need to look at neighboring items, like comparing each day’s temperature to the day before. Then you need the index to reach temps[i - 1]:

temps = [12, 15, 11, 18, 20]
for i in range(1, len(temps)):
    change = temps[i] - temps[i - 1]
    print(f"Day {i}: {change:+}")

# Output:
# Day 1: +3
# Day 2: -4
# Day 3: +7
# Day 4: +2Code language: Python (python)

Notice how the range starts at 1 here, because day 0 has no day before it to compare with.

Python range vs. list

Ranges have one big advantage over lists: they use a small, fixed amount of memory, because they are calculated on the fly. A range is lazy. It only remembers its start, stop, and step, and works out each number at the moment you ask for it. So range(10_000_000) takes the same amount of memory as range(10). A list, on the other hand, stores every single number. You can see the difference with sys.getsizeof(), which reports how many bytes an object takes:

import sys

print(sys.getsizeof(range(10)))
print(sys.getsizeof(range(10_000_000)))
print(sys.getsizeof(list(range(10_000_000))))

# Output:
# 48
# 48
# 80000056Code language: Python (python)

Both ranges take 48 bytes. The list with ten million numbers takes 80 megabytes, and that’s only the list itself, not counting the integer objects it points to. The exact numbers can differ a little between Python versions and computers, but the difference in scale won’t.

Because a range knows its start, stop, and step, it can answer some questions without going through the numbers. Asking for the length, the last number, or whether a number is in the range is instant, even for a range of a trillion numbers:

big = range(1_000_000_000_000)
print(len(big))
print(big[-1])
print(999_999_999_999 in big)

# Output:
# 1000000000000
# 999999999999
# TrueCode language: Python (python)

Try that with list(range(1_000_000_000_000)) and your computer runs out of memory long before it finishes.

In Python 2 this wasn’t the case. The regular range would materialize into an actual list of numbers. At some point, xrange was introduced. In Python 3, it was decided that xrange would become the default for ranges, and the old materialized range was dropped. In addition, some extra features were added, like the ability to slice and compare ranges.

The name xrange does not exist in Python 3. If you encounter it, you’re likely looking at Python 2 code. I created an article on this subject if you want to convert it to Python 3 code.

How a Python range works

Now that you’ve seen range in action, I’ll show you how ranges work internally. There’s not much magic to it! If you don’t fully understand iterators and iterables yet, feel free to skip this part, or read up on them first and come back.

A range is an iterable object, and this object can return an iterator that keeps track of its current state. Suppose you create a range with the call range(3). This returns an object of type range with your requested settings. Internally, a Python range sets the following values:

  • the start value to 0,
  • the stop value to 3,
  • and the step value to the default value of 1.

When you start iterating a range object, the range object returns an iterator. This iterator uses the values above but has one extra: a counter variable i, which starts at 0 and increases on each iteration.

On each request for the next number, the iterator looks at its internal state and calculates the next number to return. The next value is calculated like this:

next = start + step*i.

By default, range takes steps of size one:

  • So on the first call, the calculation that is made is this: 0 + 1*0 == 0. Range returns 0 and the counter i is increased by 1, making i == 1
  • On the 2nd call, it makes the same calculation with its current state: 0 + 1*1 == 1. It again increases the counter i to 2.
  • The 3rd call returns 2, since 0 + 1*2 == 2.
  • We’ve now reached the end of the range. As explained in my article on Python iterators, any following call to the iterator raises a StopIteration exception, signaling that there are no more items.

If you give your range a step size of 2 with range(0, 5, 2), you can repeat the calculations from above and see that the returned values are now: 0, 2, and 4.

Demonstrated with code

You can see this process in code. In the example below, I first create a range object. Next, I manually request an iterator from the range object using the built-in iter() function, and ask it for the next value with another built-in function: next(). After 3 calls, the iterator is exhausted and raises a StopIteration exception:

my_range = range(0, 3)

# Obtain the iterator. In a for-loop, this
# step happens automatically
my_iter = iter(my_range)

# Request values from the iterator
print(next(my_iter))
print(next(my_iter))
print(next(my_iter))
print(next(my_iter))Code language: Python (python)

Output:

0
1
2
Traceback (most recent call last):
  File "example.py", line 11, in <module>
    print(next(my_iter))
          ~~~~^^^^^^^^^
StopIterationCode language: Python (python)

Python range: Iterable or iterator?

A range object is an iterable, meaning that it is an object that can return an iterator. As seen in the example above, you can obtain this iterator manually with the iter() function. When used in a for-loop, the loop requests the iterator automatically and takes care of everything.

The range object (the iterable) returns a new iterator each time you ask for one. That’s why you can loop over the same range as often as you like, and it starts from the beginning every time:

numbers = range(3)
print(list(numbers))
print(list(numbers))

# Output:
# [0, 1, 2]
# [0, 1, 2]Code language: Python (python)

An iterator, by contrast, is used up after one pass. To understand this in detail, read up on iterables and iterators.

Python Ranges in practice

What follows are some common and some less common operations on and with ranges that you might need in practice, besides using them for loops.

Testing for membership

Ranges behave like a regular collection, so you can test them for membership of a certain value. For example, to test if a number is part of a range:

my_number = 10
print(my_number in range(0, 11))
print(7 in range(0, 20, 2))

# Output:
# True
# FalseCode language: Python (python)

The second check is False because a range with step 2, starting at 0, only contains even numbers.

Length and indexing

A range is a sequence, just like a list. That means you can ask for its length with len(), get a number at a certain position with square brackets, and find the position of a number with .index():

r = range(0, 20, 5)
print(len(r))
print(r[1])
print(r[-1])
print(r.index(15))

# Output:
# 4
# 5
# 15
# 3Code language: Python (python)

Range slicing

Although I’m not sure you’ll ever need this (I never used it in practice), this feature is pretty cool. Just like you can slice lists and other sequence types, you can also slice a range object. When doing so, you might expect the range to turn into a list. That’s not the case. Instead, a new range object is calculated and returned, as you can see in the following examples on the Python REPL:

>>> range(0, 5)[2:4]
range(2, 4)
>>> range(0, 5)[4:2:-1]
range(4, 2, -1)
>>> range(0, 10)[2:8:2]
range(2, 8, 2)Code language: Python (python)

Comparing ranges

Finally, ranges can be compared as well. When comparing ranges, they are compared as sequences. You might expect that Python only checks if they are the same object. This used to be true until Python 3.3 came out. Since then, two different ranges that result in the same sequence are equal in Python.

Here are some examples on the REPL:

>>> range(0, 2) == range(0, 3)
False
>>> range(0) == range(4, 2)
True
>>> range(0, 3, 2) == range(0, 4, 2)
TrueCode language: Python (python)

In the second example, a range with start 4 and stop 2 results in a range of length zero; hence it is equal to range(0). In the last example, both ranges contain 0 and 2, so they’re equal even though their stop values differ.

Range with floats

Range only works with integers. If you pass it a float, for example to count in steps of 0.1, you get an error:

range(0, 1, 0.1)

# TypeError: 'float' object cannot be interpreted as an integerCode language: Python (python)

The simplest workaround is to count in whole numbers and divide:

steps = [i / 10 for i in range(0, 5)]
print(steps)

# Output:
# [0.0, 0.1, 0.2, 0.3, 0.4]Code language: Python (python)

Dividing whole numbers like this avoids the rounding errors you’d get from adding 0.1 over and over.

Conclusion

You’ve learned how to use Python’s range in a for-loop, with a stop value, a start value, and a step. You know the stop value is never included, so you add 1 when you want an inclusive range, and you use a negative step or reversed() to count backwards. You can turn a range into a list with list(), and you’ve seen why enumerate() often beats range(len(...)). Finally, you’ve learned that a range is lazy, which is why even a huge range takes almost no memory.

If you want to learn more about ranges, these resources might be of interest:

Related articles

2 thoughts on “Python range() Function: How-To Tutorial With Examples”

  1. Hello,

    When I paste this in Python3.13.7:

    for i in range(0, 6, 2):
    print(i, end=” “)

    The following happens:

    >>> for i in range(0, 6, 2):
    … print(i, end=” “)
    …
    >>> 4

    What’s happening?

    • It should absolutely print 0 2 4. I just checked to be absolutely sure in Python 3.13.2 as well.
      So it is likely some copy/paste issue. What’s happening is that the print(i) is not in the loop, but at the end of the program. So it only prints the last value of i, which is 4.
      If it is an indentation issue, I would have expected you to get this error: “IndentationError: expected an indented block after ‘for’ statement on line 1”

Leave a Comment