If you’ve ever programmed in Python, you have a 96.2% chance of having used the range()
function. As you have a 96.1% chance of knowing, said function returns a list containing an arithmetic progression of integers, exactly as the documentation says. For example, range(4)
returns [0, 1 2, 3]
.
If you’ve ever bothered to look at the docstring, you will see that it goes thus:
range([start,] stop[, step]) -> list of integers
Return a list containing an arithmetic progression of integers.
range(i, j) returns [i, i+1, i+2, ..., j-1]; start (!) defaults to 0.
When step is given, it specifies the increment (or decrement).
For example, range(4) returns [0, 1, 2, 3]. The end point is omitted!
These are exactly the valid indices for a list of 4 elements.
Every reader will notice that there’s a mandatory parameter, stop
, and the rest are optional. The curious among you (or those who could be bothered to read it), will also notice, that the start
parameter is optional. How can this be? We all know that you can’t have optional keyword arguments before non-optional arguments in Python! What trickery does range()
use to do our bidding?
Luckily, after painstakingly downloading the source and poring through the 1221 lines of rangeobject.c
so you don’t have to, I now have the explanation of how such magic is performed. It’s really not very magical, and certainly nothing you can’t do in pure Python code.
Unlike my first instinct, it doesn’t use any fancy C magic to do that (well, it sort of does, but it’s just an implementation detail). How the range() function gets its optional first keyword argument is simple: The arguments aren’t keyword arguments at all:
>>> range(stop=2)
TypeError: range() takes no keyword arguments
After finding this out, it’s pretty easy to see how they got the optional first argument: They didn’t. They just check for the number of arguments passed and act accordingly. If there’s only one argument passed, it’s the stop
parameter, two are start
and stop
, and three are start
, stop
and range
.
I hope this foray into the Python source has taught you the valuable lesson it’s taught me: Don’t download the source before checking if the arguments actually are keyword arguments.