Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

range non-default parameter follows default one

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?

like image 941
Dimitris Fasarakis Hilliard Avatar asked May 16 '17 10:05

Dimitris Fasarakis Hilliard


People also ask

Can non-default argument follow default argument?

The error “SyntaxError: non-default argument follows default argument” occurs when you specify a default argument before a non-default argument. To solve this error, ensure that you arrange all arguments in your function definition such that the default arguments come after the non-default arguments.

Can there be more than one default parameter in a function?

Only one parameter of a function can be a default parameter. B. Minimum one parameter of a function must be a default parameter.

What are the parameters that are default?

Default parameter in Javascript The default parameter is a way to set default values for function parameters a value is no passed in (ie. it is undefined ). In a function, Ii a parameter is not provided, then its value becomes undefined . In this case, the default value that we specify is applied by the compiler.

What is the default value of parameter?

In JavaScript, a parameter has a default value of undefined. It means that if you don't pass the arguments into the function, its parameters will have the default values of undefined .


1 Answers

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.

like image 112
MSeifert Avatar answered Sep 25 '22 08:09

MSeifert