Entonces estoy escribiendo una función que toma una lista opcional y la extiende a la longitud especificada. En lugar de escribirlo como foo (n, list = None) me preguntaba cómo podría emular el comportamiento de la función de rango de Python que funciona como:

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

Es decir, con el parámetro predeterminado primero. Como referencia, intentar configurar esto ingenuamente devuelve un error de sintaxis:

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

Entonces me pregunto, ¿está esto codificado dentro del rango? ¿O se puede emular este comportamiento?

12
Ceasar Bautista 11 jul. 2011 a las 03:50

5 respuestas

La mejor respuesta

Ellas no son argumentos de palabras clave reales.

Si hay un argumento, es el límite.

Si hay dos argumentos, el primero es el valor inicial y el segundo es el límite.

Si hay tres argumentos, el primero es el valor inicial, el segundo es el límite y el tercero es el paso.

8
MRAB 10 jul. 2011 a las 23:57

Considerar:

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
Alok Singhal 11 jul. 2011 a las 00:01

Python implementa range() al observar el número de argumentos. No debería ser demasiado difícil escribir una versión de Python de este código

de 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;
}
6
John La Rooy 11 jul. 2011 a las 00:10

Otros han mostrado cómo se puede hacer usando el conteo de argumentos. Sin embargo, si lo implementara yo mismo en Python, lo haría más así.

def range(start, limit=None, stride=1):
    if limit is None:
        start, limit = 0, start
    # ...
10
kindall 11 jul. 2011 a las 05:23

Una forma de escribir range en Python puro sería

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
8
Ismail Badawi 10 jul. 2011 a las 23:58