Why does range allow a non-default parameter (stop
) to follow a default parameter (start
)?
Case in point:
>>> r = range(1, 2, 3)
>>> print(r.start, r.stop, r.step)
1 2 3
>>> r = range(10)
>>> print(r.start, r.stop, r.step)
0 10 1
Trying to emulate the signature is an obvious violation:
def my_range(start=0, stop, end=1):
pass
I understand that the fact it is implemented in C
probably allows for behavior that would be a violation in Pythonland.
I'm guessing this was done to make the API more user-friendly but, I didn't find any sources to back it up (The source code doesn't tell much and PEP 457 only states how range
is odd). Does anyone know why this was done?
I think the question is based on a wrong premise:
I understand that the fact it is implemented in C probably allows for behavior that would be a violation in Pythonland.
It's implemented in C but the behaviour isn't a violation in "Pythonland". The signature in the documentation is just incorrect (not actually incorrect, it's an approximation of the "real signature" - that can be easily understood).
For example range
doesn't even support named parameters - but according to the documentation it should:
>>> range(stop=10)
TypeError: range() does not take keyword arguments
So the implementation is more along the lines of:
class range(object):
def __init__(self, *args):
start, step = 0, 1
if len(args) == 1:
stop = args[0]
elif len(args) == 2:
start, stop = args
elif len(args) == 3:
start, stop, step = args
That's valid Python and (roughly) does what range
internally does (the actual implementation (CPython, Python 3.6.1) could be slightly different so don't take that class to seriously).
However a signature like range(*args)
is probably not really helpful for users (especially new users that don't even know what *args
means). Having a documentation that says range
has 2 signatures: range(stop)
and range(start, stop[, step])
may not be (technically) accurate but it "explains" how the signature is interpreted.
As for the why: I don't have any creditable sources but I quickly scanned my code:
I use range(stop)
much more often than range(start, stop)
or range(start, stop, step)
. So the one argument case was probably special and common enough to have a convenience for it. It would be pretty annoying to always write range(0, stop)
all over the place.