Módulo de extensión de Python con número variable de argumentos

stoy tratando de descubrir cómo en los módulos de extensión C tener una variable (y tal vez) un número bastante grande de argumentos para una función.

Leyendo sobre PyArg_ParseTuple parece que tiene que saber cuántos aceptar, algunos obligatorios y otros opcionales, pero todos con su propia variable. Estaba esperando PyArg_UnpackTuple podría manejar esto, pero parece que solo me da errores de bus cuando trato de usarlo en lo que parece ser el camino equivocado.

omo ejemplo, tome el siguiente código de Python que uno podría querer convertir en un módulo de extensión (en C).

def hypot(*vals):
    if len(vals) !=1 :
        return math.sqrt(sum((v ** 2 for v in vals)))
    else: 
        return math.sqrt(sum((v ** 2 for v in vals[0])))

Esto se puede llamar con cualquier número de argumentos o iterar sobre,hypot(3,4,5), hypot([3,4,5]) yhypot(*[3,4,5]) todos dan la misma respuesta.

El inicio de mi función C se ve así

static PyObject *hypot_tb(PyObject *self, PyObject *args) {
// lots of code
// PyArg_ParseTuple or PyArg_UnpackTuple
}

Many piensa en yasar11732. Aquí para el siguiente chico hay un módulo de extensión completamente funcional (_toolboxmodule.c) que simplemente toma cualquier número o argumentos enteros y devuelve una lista compuesta de esos argumentos (con un nombre pobre). Un juguete pero ilustra lo que había que hacer.

#include <Python.h>

int ParseArguments(long arr[],Py_ssize_t size, PyObject *args) {
    /* Get arbitrary number of positive numbers from Py_Tuple */
    Py_ssize_t i;
    PyObject *temp_p, *temp_p2;

    for (i=0;i<size;i++) {
        temp_p = PyTuple_GetItem(args,i);
        if(temp_p == NULL) {return NULL;}

        /* Check if temp_p is numeric */
        if (PyNumber_Check(temp_p) != 1) {
            PyErr_SetString(PyExc_TypeError,"Non-numeric argument.");
            return NULL;
        }

        /* Convert number to python long and than C unsigned long */
        temp_p2 = PyNumber_Long(temp_p);
        arr[i] = PyLong_AsUnsignedLong(temp_p2);
        Py_DECREF(temp_p2);
    }
    return 1;
}

static PyObject *hypot_tb(PyObject *self, PyObject *args)
{
    Py_ssize_t TupleSize = PyTuple_Size(args);
    long *nums = malloc(TupleSize * sizeof(unsigned long));
    PyObject *list_out;
    int i;

    if(!TupleSize) {
        if(!PyErr_Occurred()) 
            PyErr_SetString(PyExc_TypeError,"You must supply at least one argument.");
        return NULL;
    }
    if (!(ParseArguments(nums, TupleSize, args)) { 
        free(nums);
        return NULL;
    }

    list_out = PyList_New(TupleSize);
    for(i=0;i<TupleSize;i++)
        PyList_SET_ITEM(list_out, i, PyInt_FromLong(nums[i]));
    free(nums);
    return (PyObject *)list_out;
}

static PyMethodDef toolbox_methods[] = {
   { "hypot", (PyCFunction)hypot_tb, METH_VARARGS,
     "Add docs here\n"},
    // NULL terminate Python looking at the object
     { NULL, NULL, 0, NULL }
};

PyMODINIT_FUNC init_toolbox(void) {
    Py_InitModule3("_toolbox", toolbox_methods,
                     "toolbox module");
}

En Python entonces es:

>>> import _toolbox
>>> _toolbox.hypot(*range(4, 10))
[4, 5, 6, 7, 8, 9]

Respuestas a la pregunta(1)

Su respuesta a la pregunta