Python Ranges

If you've used Python for any length of time on the Pi, it's almost certain that you've come across range. Python's range is simply that: a range of values meeting certain criteria.

Range accepts three arguments:

  • Start - The number to start from
  • Stop - The number to stop at, not included in the range
  • Step - The step size of the range

Differences between Python 2 and Python 3

Range is one of the many gotchas in the transition between Python 2 and 3. Here's why:

In Python 2 a range returns a list:

>>> range(0,5)
[0, 1, 2, 3, 4]

In Python 2, the range function will immediately figure out every single number contained within the range you've asked for and will return it as a list.

In Python 3 a range returns an "virtual sequence of numbers":

>>> range(0,5)
range(0, 5)

Seeing the Python 2 method as wasteful, Python 3 changed range to use the "lazy way." Python 3's lazy ranges will only give you a value when asked for it, so if you do this:

>>> my_range = range(0,5)
>>> do_something_completely_different()

Python 3 simply wont waste time figuring out the values in my_range. In Python 2, however, it will go off and create a list at its leisure.

In most cases, you'll never run into anything on the Pi where the speed of lazy ranges versus ranges is a problem, or even a concern, but it's part of what helps make Python 3 faster and the lazy way is generally considered to be the right way amidst the programming neckbeards.

Ranges for fun and profit

So now you know exactly what a range is for, what can we do with it that matters to you? Let's practice some multiplication tables:

>>> def multiplication_table(table, results):
...     list(range(0,(table*results)-1,table))
>>> multiplication_table(3, 5)
[0, 3, 6, 9, 12]
>>> multiplication_table(7, 5)
[0, 7, 14, 21, 28]

Superb. We've now got a Python one liner for generating ranges we can practise with. Can we make it more useful?

def multiplication_table(number, number_of_items):
  return list(range(0, (number*(number_of_items+1)), number))[1:]

Ta daa:

>>> multiplication_table(9, 10)
[9, 18, 27, 36, 45, 54, 63, 72, 81, 90]

Reverse my range!

If you play with ranges for a while, you might find yourself trying something like this:

>>> list(range(9,0))

Whoa? What happened there? Well the default value for the third argument of range is 1. If you try to count from 9 down to 0 by counting up 1 every time then you're going to have a very, very hard time getting there:

  • 9
  • 10 ?
  • 11 ... eek!
  • 12 ... this isn't going well
  • ...
  • 20 ... well, we've one one 0?

There's a simple fix for this:

>> list(range(9,0,-1))
[9, 8, 7, 6, 5, 4, 3, 2, 1]

This has an interesting side-effect:

  • If you create a range from 0 to 9 you'll get 0, 1, 2, 3, 4, 5, 6, 7, 8.
  • If you create a range from 9 to 0 you'll get 9, 8, 7, 6, 5, 4, 3, 2, 1.

You can either adjust your start/end accordingly, or optionally used the reversed function like so:

>>> list(reversed(range(0,9)))
[8, 7, 6, 5, 4, 3, 2, 1, 0]

The reversed function will give you a reversed list, so you can count from 0 to 9 and then reverse it to get a countdown. reversed can also be used with lists, and isn't limited to just reversing lists of numbers.

What about xrange?

Unless you have a really, really good reason, don't use it. Let's ask Python 3 why:

>>> xrange(0,10)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'xrange' is not defined

In Python 3, range was updated to behave in the same way as xrange did in Python 2, rendering xrange obsolete. It's simply been removed.

xrange is one of those classic examples of language bloat, where things start to creep in that make sense but inevitably just complicate what's a really simple problem. In your Python adventures you're very unlikely to find an example where you're using exclusively Python 2 and need the extra performance that xrange can potentially offer. Stick with range and ensure your code will run in Python 3.

That's all folks!

Search above to find more great tutorials and guides.

Plasma 2040

Swathe everything in rainbows with this all-in-one, USB-C powered controller for WS2812/Neopixel and APA102/Dotstar addressable LED strip.