Как встроенная функция диапазона принимает один аргумент или три?

Я хотел бы знать, может ли кто-нибудь сказать мне, как функция диапазона может принимать либо: один аргумент, range(stop), либо range(start, stop), или range(start, stop, step). Использует ли аргумент variadic, например *arg, для сбора аргументов, а затем использует ряд операторов if для назначения правильных значений в зависимости от количества предоставленных аргументов? По существу, range() указывает, что если есть один аргумент, то он устанавливается как аргумент stop, или если есть два, то они start и stop, или если их три, то они устанавливают такие как stop, start и step соответственно? Я хотел бы знать, как это можно было бы сделать, если бы кто-то написал диапазон в чистом Cpython. Спасибо!!!

Обновление: Я не уточнил, когда я изначально задал вопрос, что хотел узнать ответ на Cpython. Независимо благодарю вас за все ваши ответы! Я нашел их достаточно просвещенными. Это обратная связь, которая заставляет меня любить stackoverflow и людей, которые делают его настолько особенным!

Ответ 1

Диапазон принимает, 1, 2 или 3 аргумента. Это может быть реализовано с помощью def range(*args) и явного кода для создания исключения, когда он получает 0 или более трех аргументов.

Он не может быть реализован с аргументами по умолчанию, потому что вы не можете иметь значение по умолчанию по умолчанию, например. def range(start=0, stop, step=1). Это связано с тем, что python должен выяснить, что означает каждый вызов, поэтому, если вам нужно было бы вызвать с двумя аргументами, python понадобилось бы какое-то правило, чтобы выяснить, какой аргумент по умолчанию вы переопределяете. Вместо того, чтобы иметь такое правило, оно просто не разрешено.

Если вы хотите использовать аргументы по умолчанию, вы можете сделать что-то вроде: def range(start=0, stop=object(), step=1) и иметь явную проверку типа stop.

Ответ 2

Красота программного обеспечения с открытым исходным кодом заключается в том, что вы можете просто посмотреть его в источнике:

(TL; DR: да, он использует 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 answer демонстрирует, как range реализуется в C. Вам все же может быть интересно узнать, как range будет реализован, если встроенные функции Python были написаны в Python. Мы можем прочитать исходный код PyPy, чтобы узнать. Из 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

Первый аргумент space появляется в качестве аргумента во всех встроенных функциях, которые я видел, поэтому я предполагаю, что он похож на self тем, что пользователь напрямую его не передает. Из оставшихся трех аргументов два из них имеют значения по умолчанию; поэтому вы можете вызвать range с одним, двумя или тремя аргументами. Как интерпретируется каждый аргумент, зависит от количества аргументов.

Ответ 4

Простая реализация python 2 xrange(). Работает точно так же.

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))