Use range() or xrange() to Track Loop Index
In the last lesson, you saw the sort of loop you might expect a developer coming from a C-style language to write in Python. In this lesson, you’ll see how you can use the range()
and xrange()
built-ins to make your loops more Pythonic.
The original loop keeps track of the loop index manually, which isn’t very Pythonic. You can instead keep track automatically if you use range()
in Python 3 and xrange()
in Python 2. A range
object is a generator in Python.
00:00
The first thing I’m going to change here is how this loop was keeping track of the loop index manually—so we’re incrementing this little counter variable called i
, and that’s not very Pythonic.
00:12
Thankfully, there’s a way in Python to do that automatically using the range()
builtin. So in this case, I’m going to call the range()
function with the length of my container—which is not ideal, just kind of foreshadowing some things, but we’re going to change that right after. So, if I call this, what I get back is a range
object. Now, a range
object is a generator in Python, so it’s going to be generating these individual indexes from 0
all the way to 2
—because it’s not going to include the right-hand-side element.
00:44
And actually, if you want to see what this looks like, I can call list()
on this range
object, and that’s going to kind of go through the whole generator and it’s going to give me a list with the elements generated by the generator.
00:56
So as you can see here, this range
object would go from 0
all the way to 3 - 1, which is 2
. And what I can do now is I can refactor that loop using the built-in range()
function, and that allows me to get rid of the index tracking.
01:13
Again, this is going to work exactly the same—what you can see here now, we’re using this range
object to generate these loop indexes one by one.
01:21
Now by the way, if you’re on Python 2, you’ll want to use the xrange()
builtin to actually get a generator because if you’re using range()
in Python 2, it will pre-create the whole range as an actual list, which is going to be bad for memory usage. So, if you’re going to create a huge range in Python 2, this would actually pre-create all of those elements, which is probably not what you want.
01:43
So, on Python 2 you’ll want to use the xrange()
builtin. It does exactly the same as range()
, but the key difference is that it actually returns a generator versus returning the actual list.
Dan Bader RP Team on Dec. 13, 2023
Sure thing, here’s an example! Let’s take a look at the Python 2 behavior first:
>>> # This is Python 2:
>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> xrange(10)
xrange(10)
>>> list(xrange(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Now, compare this to how Python 3 behaves:
>>> # This is Python 3.11:
>>> range(10)
range(0, 10)
>>> list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> xrange(10)
Traceback (most recent call last):
NameError: name 'xrange' is not defined. Did you mean: 'range'?
Note how range(10)
returns a “pre-created” list of elements in Python 2, and a range
object in Python 3.
I used a relatively short range of 0..9 here, which doesn’t really matter from a performance standpoint. But we can imagine that a huge range was used instead, like range(100000000000)
, where creating all those list elements would impact performance.
Nivaldo Junior on Aug. 14, 2024
I might got bit lost on therm “Pythonic”, but I have to ask. Isn’t
for i in list_items: print(i)
more “pythonic” than
for i in range(len(list_items)):
print(list_items[i])
Why do I need so many method for the same result?
Dan Bader RP Team on Aug. 15, 2024
Yep, you only need range()
if you require access to the loop index. If you don’t need the loop index then I’d also prefer this shorter and more “Pythonic” variant:
for i in list_items:
print(i)
Become a Member to join the conversation.
jayantsing on Dec. 13, 2023
Hello, Is it possible to see an example of pre creation of list using range() in Python 2. It will be helpful to understand the difference from scratch.
Appreciate the explanation.