How can the built-in range function take a single argument or three?

风格不统一 提交于 2019-12-17 16:34:07

问题


I would like to know, if anyone can tell me, how the range function can take either: a single argument, range(stop), or range(start, stop), or range(start, stop, step). Does it use a variadic argument like *arg to gather the arguments, and then use a series of if statements to assign the correct values depending on the number of arguments supplied? In essence, does range() specify that if there is one argument, then it set as the stop argument, or if there are two then they are start, and stop, or if there are three then it sets those as stop, start, and step respectively? I'd like to know how one would do this if one were to write range in pure Cpython. Thanks!!!

Update: I did not clarify, when I initially asked the question, that I wanted to know the answer in Cpython. Regardless thank you for all of your responses! I found them all fairly enlightening. This is the kind of feedback that makes me love stackoverflow and the people that make it so special!


回答1:


Range takes, 1, 2, or 3 arguments. This could be implemented with def range(*args), and explicit code to raise an exception when it gets 0 or more than 3 arguments.

It couldn't be implemented with default arguments because you can't have a non-default after a default, e.g. def range(start=0, stop, step=1). This is essentially because python has to figure out what each call means, so if you were to call with two arguments, python would need some rule to figure out which default argument you were overriding. Instead of having such a rule, it's simply not allowed.

If you did want to use default arguments you could do something like: def range(start=0, stop=object(), step=1) and have an explicit check on the type of stop.




回答2:


The beauty of open-source software is that you can just look it up in the source:

(TL;DR: yes, it uses varargs)

if (PyTuple_Size(args) <= 1) {
    if (!PyArg_UnpackTuple(args, "range", 1, 1, &stop))
        return NULL;
    stop = PyNumber_Index(stop);
    if (!stop)
        return NULL;
    start = PyLong_FromLong(0);
    if (!start) {
        Py_DECREF(stop);
        return NULL;
    }
    step = PyLong_FromLong(1);
    if (!step) {
        Py_DECREF(stop);
        Py_DECREF(start);
        return NULL;
    }
}
else {
    if (!PyArg_UnpackTuple(args, "range", 2, 3,
                           &start, &stop, &step))
        return NULL;

    /* Convert borrowed refs to owned refs */
    start = PyNumber_Index(start);
    if (!start)
        return NULL;
    stop = PyNumber_Index(stop);
    if (!stop) {
        Py_DECREF(start);
        return NULL;
    }
    step = validate_step(step);    /* Caution, this can clear exceptions */
    if (!step) {
        Py_DECREF(start);
        Py_DECREF(stop);
        return NULL;
    }
}



回答3:


lqc's answer demonstrates how range is implemented in C. You may still be curious about how range would be implemented if Python's built in functions were written in Python. We can read PyPy's source code to find out. From pypy/module/__builtin__/functional.py:

def range_int(space, w_x, w_y=NoneNotWrapped, w_step=1):
    """Return a list of integers in arithmetic position from start (defaults
to zero) to stop - 1 by step (defaults to 1).  Use a negative step to
get a list in decending order."""

    if w_y is None:
        w_start = space.wrap(0)
        w_stop = w_x
    else:
        w_start = w_x
        w_stop = w_y

The first argument, space, appears as an argument in all the built-in functions I saw, so I'm guessing it's kind of like self in that the user does not directly supply it. Of the remaining three arguments, two of them have default values; so you can call range with one, two, or three arguments. How each argument is interpreted depends upon how many arguments were supplied.




回答4:


A simple implementation of python 2 xrange(). Works exactly the same.

def xrange(*args):
    start,step=-1,1
    lst1=[]

    for i in args:
        if type(i) is not int:
            return "Cannot interprate '{}' as integer!".format(type(i).__name__)

    if len(args)<1:
        return "c_range Must have at least one argument"

    if len(args)==1:
        stop=(args[0]-step)

        while start<stop:
            start+=(step)
            lst1.append(start)

        return lst1

    if len(args)==2:
        start=(args[0]-step)
        stop=(args[1]-step)

        while start<stop:
            start+=(step)
            lst1.append(start)

        return lst1

    if len(args)==3:
        step=args[2]

        if step==0:
            return "Argument 3 should not be zero!"

        start=(args[0]-(step))
        stop=(args[1]-(step))

        if start<stop:
            while start<stop:
                if start<stop:
                    start+=step
                    lst1.append(start)
        else:
            while start>stop and step<0:
                start+=step
                lst1.append(start)

        return lst1

    if length(args)>3:
        return "Expected at most three arguments,got {}".format(len(args))


来源:https://stackoverflow.com/questions/13366293/how-can-the-built-in-range-function-take-a-single-argument-or-three

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!