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

Итак, я пишу функцию, которая принимает дополнительный список и расширяет его до указанной длины. Вместо того, чтобы писать его как foo (n, list = None), мне было интересно, как я могу эмулировать поведение функции диапазона Python, которая работает следующим образом:

>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> range(5, 10)
[5, 6, 7, 8, 9]

То есть с параметром по умолчанию. Для справки, пытающейся наивно установить это, возвращается синтаксическая ошибка:

def foo(x=10, y):
    return x + y
SyntaxError: non-default argument follows default argument

Так что мне интересно, это жестко закодировано в диапазоне? Или можно эмулировать такое поведение?

Ответ 1

Они не являются аргументами реального ключевого слова.

Если есть один аргумент, это предел.

Если есть два аргумента, первым является начальное значение, а второе - предел.

Если есть три аргумента, первое - начальное значение, второе - предел, а третий - шаг.

Ответ 2

Один способ написать range в чистом python будет

def range(*args):
    if len(args) > 3:
        raise TypeError, 'range expected at most 3 arguments, got %d' % len(args)
    if len(args) == 2:
        return range(args[0], args[1], 1)
    if len(args) == 1:
        return range(0, args[0], 1)
    else:
        # actual code for range(start, stop, step) here

Ответ 3

Другие показали, как это можно сделать, используя подсчет аргументов. Если бы я сам реализовал его на Python, я бы сделал это более как это.

def range(start, limit=None, stride=1):
    if limit is None:
        start, limit = 0, start
    # ...

Ответ 4

Python реализует range(), просматривая количество аргументов. Не стоит слишком писать питонную версию этого кода

from rangeobject.c:

static PyObject *
range_new(PyTypeObject *type, PyObject *args, PyObject *kw)
{
    rangeobject *obj;
    long ilow = 0, ihigh = 0, istep = 1;
    unsigned long n;

    if (!_PyArg_NoKeywords("xrange()", kw))
        return NULL;

    if (PyTuple_Size(args) <= 1) {
        if (!PyArg_ParseTuple(args,
                        "l;xrange() requires 1-3 int arguments",
                        &ihigh))
            return NULL;
    }
    else {
        if (!PyArg_ParseTuple(args,
                        "ll|l;xrange() requires 1-3 int arguments",
                        &ilow, &ihigh, &istep))
            return NULL;
    }
    if (istep == 0) {
        PyErr_SetString(PyExc_ValueError, "xrange() arg 3 must not be zero");
        return NULL;
    }
    n = get_len_of_range(ilow, ihigh, istep);
    if (n > (unsigned long)LONG_MAX || (long)n > PY_SSIZE_T_MAX) {
        PyErr_SetString(PyExc_OverflowError,
                        "xrange() result has too many items");
        return NULL;
    }

    obj = PyObject_New(rangeobject, &PyRange_Type);
    if (obj == NULL)
        return NULL;
    obj->start = ilow;
    obj->len   = (long)n;
    obj->step  = istep;
    return (PyObject *) obj;
}

Ответ 5

Рассмотрим:

def f(*args):
    nargs = len(args)
    if nargs == 1:
        start = 0
        end = args[0]
        step = 1
    elif nargs == 2:
        start = args[0]
        end = args[1]
        step = 1
    elif nargs == 3:
        start = args[0]
        end = args[1]
        step = args[2]
    else:
        raise TypeError('wrong number of arguments')

    return g(start, end, step)