Quick Answer
This question popped up when I first started learning Python, and I think it worthwhile to document the method here. Only one check is used to simulate original behavior.
def list_range(start, stop=None, step=1):
if stop is None:
start, stop = 0, start
return list(range(start, stop, step))
I think this solution is a bit more elegant than using all keyword arguments or *args
.
Explanation
Use a Sentinel
The key to getting this right is to use a sentinel object to determine if you get a second argument, and if not, to provide the default to the first argument while moving the first argument to the second.
None
, being Python's null value, is a good best-practice sentinel, and the idiomatic way to check for it is with the keyword is
, since it is a singleton.
Example with a proper docstring, declaring the signature/API
def list_range(start, stop=None, step=1):
'''
list_range(stop)
list_range(start, stop, step)
return list of integers from start (default 0) to stop,
incrementing by step (default 1).
'''
if stop is None:
start, stop = 0, start
return list(range(start, stop, step))
Demonstration
>>> list_range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> list_range(5, 10)
[5, 6, 7, 8, 9]
>>> list_range(2, 10, 2)
[2, 4, 6, 8]
And it raises an error if no arguments are given, unlike the all-keyword solution here.
Caveat
By the way, I hope this is only considered from a theoretical perspective by the reader, I don't believe this function is worth the maintenance, unless used in a central canonical location to make code cross-compatible between Python 2 and 3. In Python, it's quite simple to materialize a range into a list with the built-in functions:
Python 3.3.1 (default, Sep 25 2013, 19:29:01)
[GCC 4.7.3] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]