Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

This lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Advanced Uses of range()

So far, you’ve seen how you can use the range function to generate a list of integers. It might appear that using range() in a for loop is equivalent to using a list of incrementing or decrementing values, but that’s not the case. There are some important similarities and differences. range() is its own class in Python:

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

range isn’t a list, but it dynamically manages a list in memory.

00:00 So far, we’ve seen how we can use the range() function to generate a list of integers. It would appear that using range() in a for loop is equivalent to using a list of incrementing or decrementing values—but that’s actually not the case.

00:20 There are some important similarities and differences. To explain them, I’m going to use the interactive shell.

00:28 I’ve been saying that the range() function generates a list of numbers, which is true, but it’s not the same as just having a list. To prove this, I’ll use the type() function, passing in a call to range().

00:47 As you can see, range is its own class in Python. range is clearly not a list, but instead it dynamically manages a list in memory.

01:01 Even though range itself isn’t an iterable type, we can still use common indexing and slicing techniques as if it were an iterable. For example, I can say range(3) and get the element at index 1, which is 1. Now take a look at this.

01:25 I’m going to slice a range like this, range(6) from 2 to 5. This is the same way we would slice a string or a list in Python, but this probably wasn’t what you were expecting. Rather than exposing the underlying list, slicing a range just returns another range that can generate the list we need.

01:54 But why does this even matter? One word: performance. In Python 3, range() is lazy, which means that it generates new values on a need-by-need basis, rather than when execution reaches the range() call.

02:13 If the whole list of numbers isn’t needed by our program, it isn’t stored in memory, which can result in some noticeable performance improvements in very large programs.

02:25 If we want to force the range() function to spit out the underlying list, we can simply pass it in to the built-in list() function just like this.

02:37 Back in Python 2, things used to be a little bit different. range() would generate the numbers all at once and xrange() would generate the numbers lazily.

02:51 In Python 3, range() replaces the old xrange(), and xrange() no longer exists.

Become a Member to join the conversation.