开发者

How does the Python range function have a default parameter before the actual one?

So I'm writing a function that takes an optional list and extends it to the length specified. Rather than writing it as foo(n, list=None) I was wondering how I might emulate the behavior of Python's range function which works like:

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

That is, with the default parameter first. For reference trying to naively set this up returns a syntax e开发者_如何学Pythonrror:

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

So I'm wondering, is this hard-coded into range? Or can this behavior be emulated?


Others have shown how it can be done using argument counting. If I were to implement it myself in Python, though, I'd do it more like this.

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


They aren't real keyword arguments.

If there's one argument, it's the limit.

If there are two arguments, the first is the start value and the second is the limit.

If there are three arguments, the first is the start value, the second is the limit, and the third is the stride.


One way to write range in pure python would be

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


Python implements range() by looking at the number of arguments. It shouldn't be too hard to write a Python version of this code

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;
}


Consider:

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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜