Teaching the "range" function in Python 3

Steve D'Aprano steve+python at pearwood.info
Fri Jun 30 11:58:56 EDT 2017


Hello Irv, and welcome! Good to have a teacher of Python here!


On Fri, 30 Jun 2017 06:57 am, Irv Kalb wrote:

[...]
> Now I am looking at the change in the range function.  I completely understand
> the differences between, and the reasons for, how range works differently in
> Python 2 vs Python 3.  The problem that I've run into has to do with how to
> explain what range does in Python 3.

How do you explain what xrange() does in Python 2? range() is the same.

I'm not really sure you need to explain what range() does in a lot of detail.
The technical details are a little advanced for complete beginners. Although
this might make a nice project for moderately advanced students:

"Write a class that works like range, that produces a series of numbers 0, 1, 2,
4, 8, 16, 32, ... up to some limit."

The secret is the __getitem__ method.


> In Python 2, I easily demonstrated the 
> range function using a simple print statement: 
> 
> print range(0, 10)

In Python 3, that would be:

print(list(range(10)))

(no need to give the start value if it is zero). 


> I discussed how range returns a list.  I gave many examples of different
> values being passed into range, and printing the resulting lists.

range() in Python 3 is just a lazy list. Instead of producing the values all at
once, and populating a real list (an array of values), range() produces them
only when requested.

In Python 2, you have range(10) produce a list, and fill it with 10 values:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

When you iterate over that, the interpreter says:

list[0]
"Give me the first value" -- retrieves 0 from the list

list[1]
"Give me the second value" -- retrieves 1 from the list

list[2]
"Give me the third value" -- retrieves 2 from the list

and so forth, until there are no more values. In Python 3, the range object does
NOT create a list all at once. Instead, when the interpreter says:

list[0]
"Give me the first value"

the range object (a "lazy list") calculates that the value will be 0, and
returns it; when the interpreter says:

list[6]
"Give me the seventh value"

the lazy list calculates the seventh value, namely 6, and returns it.


The advantage is not very noticeable for range(10), but consider range(10000000)
instead.


[...]
> But Python 3's version of the range function has been turned into a generator.

It actually isn't a generator, and you shouldn't use that terminology.

A generator could only be iterated over once. range objects can be iterated over
and over again. Generators don't provide random access to any item, range
objects do. Generators don't know their length:


py> def range_generator(start, end):
...     for x in range(start, end):
...             yield x
...
py> len(range_generator(0, 10))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: object of type 'generator' has no len()


but range objects do:

py> len(range(0, 10))
10
py> len(range(35, 1077, 2))
521


In fact, range objects are even sliceable!

py> range(35, 1077, 2)[50:200:3]
range(135, 435, 6)


Talking about that is probably a bit too advanced for beginners, but it does
show why you shouldn't refer to range as a generator. The most accurate
description would be a lazy sequence, or a sequence where the values are
calculated on demand.


>  Again, I understand why this happened, and I agree that this is a good
> change.  The problem is, how can I explain this concept to students who are
> just learning lists, function and function calls, etc.  The concept of how a
> generator works would be extremely difficult for them to get.  The best
> approach I have seen so far has been to build a simple for loop where I print
> out the results, like this:
> 
> for someVariable in range(0, 10):
>     print(someVariable)
> 
> However, I'm worried that this might lose a lot of students.  From their point
> of view, it might seem difficult to understand that range, which looks like a
> simple function call passing in the same values each time, returns a different
> value every time it is called.

It is important that the students understand that range is not being called each
time through the loop. It only gets called once, at the start of the for-loop.

One way to understand how it works is to think of it as a while loop:

temp = range(10)  # only called once, not every time through the loop
i = 0
while i < len(temp):
    someVariable = temp[i]
    print(someVariable)
    i += 1


Of course I don't suggest you teach your students to think of for loops in this
way -- at least not until after you've taught them while loops. I think it is
best to teach them for loops as the primitive operation, and only later tell
them that "under the hood" for loops are actually while loops.

But the important factor is that there's nothing mysterious going on with the
call to random. It returns the same value each time it is called, and it is
only called once, at the start of the loop. In some ways, it is just like a
list, except instead of pre-populating the list:

[0, 1, 2, 3, ... , 10000000]

it only calculates the values on request. The for loop tells Python to request
those values, one per loop.


> I am wondering if other teachers have run into this.  Is this a real problem? 
> If so, is there any other way of explaining the concept without getting into
> the underlying details of how a generator works?  Do you think it would be
> helpful to use the words "sequence of numbers" rather than talking about a
> list here - or would that be even more confusing to students?

Sequence is definitely the right way to go, even if it is a little more
difficult to get across. Lists have a bunch of other methods you can use, like
list.sort() and list.reverse(), which range objects don't have. They're not
lists, but they do behave a little like them.

But ultimately, since you are teaching beginners, the difference between a list
of numbers and a sequence of numbers is not very important right at the
beginning.


Hope I've given you some food for thought!





-- 
Steve
“Cheer up,” they said, “things could be worse.” So I cheered up, and sure
enough, things got worse.




More information about the Python-list mailing list