Since in python 3.X the build-id range()
function returns no longer a list but an iterable, some old code fails as I use range()
to conveniently generate lists I need.
So I try to implement my own lrange
function like this:
def lrange(start = 0, stop, step = 1):
ret = []
while start < stop:
ret.append(start)
start += step
return ret
giving me a "non-default argument follows default argument" interpreter error.
If I look at Python's range() it seems to be possible.
I posted this question mainly because I was wondering if/how one can implement a function with such a signature on his own
We can get function Signature with the help of signature() Function. It takes callable as a parameter and returns the annotation. It raises a value Error if no signature is provided. If the Invalid type object is given then it throws a Type Error.
In general the signature of a function is defined by the number and type of input arguments the function takes and the type of the result the function returns. The signature of that function, described in an abstract way would be the set {int, int, int}.
Hence, we conclude that Python Function Arguments and its three types of arguments to functions. These are- default, keyword, and arbitrary arguments.
Embrace keyword arguments in PythonConsider using the * operator to require those arguments be specified as keyword arguments. And remember that you can accept arbitrary keyword arguments to the functions you define and pass arbitrary keyword arguments to the functions you call by using the ** operator.
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
.
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.
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]
I've never actually given this much thought, but first of all, to solve your problem you should just be able to wrap range(...) with list(range(...)) in you're code.
Using keyword arguments, you could implement a signature like that since you are not required to specify the actual key when calling
def f(x=None, y=None, z=None):
print x, y, z
f(1, 2, 3)
#output: 1 2 3
Then, you could inspect the values to determine how you should handle them. So to emulate range
def f(x=None, y=None, z=None):
if z is not None: # then all values were assigned
return range(x, y, z)
elif y is not None: # then a start stop was set
return range(x, y):
else: # only one value was given
return range(x)
The point here isn't to be a wrapper for range (as above, just use list) but rather to give some insight on if one was actually trying to emulate the builtin range signature for something custom.
Also keep in mind this isn't a complete solution, f(z=1)
could cause problems with the above, so you want to provide sane defaults for each [kwarg]
while checking for required kwarg
def f(x=0, y=None, z=1):
if y is None:
raise Exception()
return range(x, y, z)
would be a little more insightful to a python method with a signature like ([start], stop, [step])
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With