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

Estoy tratando de averiguar 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.

Al leer sobre PyArg_ParseTuple , parece que debes saber cuántos aceptar, algunos obligatorios y otros opcionales, pero todos con su propia variable. Esperaba que PyArg_UnpackTuple pudiera manejar esto, pero parece que solo me da errores de bus cuando lo bash y lo uso de la forma que parece ser la incorrecta.

Como ejemplo, tome el siguiente código de Python que 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 puede ser llamado con cualquier número de argumentos o iterado sobre, hypot(3,4,5) , hypot([3,4,5]) , y hypot(*[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 } 

Muchos piensan yasar11732 . Aquí para el siguiente individuo hay un módulo de extensión completamente funcional (_toolboxmodule.c) que simplemente toma cualquier número o argumento entero y devuelve una lista compuesta de esos argumentos (con un nombre pobre). Un juguete pero ilustra lo que hay que hacer.

 #include  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] 

Yo había usado algo como esto antes. Podría ser un código malo ya que no soy un codificador de C experimentado, pero funcionó para mí. La idea es, * args es solo una tupla de Python, y puedes hacer cualquier cosa que puedas hacer con una tupla de Python. Puede consultar http://docs.python.org/c-api/tuple.html .

 int ParseArguments(unsigned 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 

Estaba llamando a esta función así:

 static PyObject * function_name_was_here(PyObject *self, PyObject *args) { Py_ssize_t TupleSize = PyTuple_Size(args); Py_ssize_t i; struct bigcouples *temp = malloc(sizeof(struct bigcouples)); unsigned long current; if(!TupleSize) { if(!PyErr_Occurred()) PyErr_SetString(PyExc_TypeError,"You must supply at least one argument."); free(temp); return NULL; } unsigned long *nums = malloc(TupleSize * sizeof(unsigned long)); if(!ParseArguments(nums, TupleSize, args)){ /* Make a cleanup and than return null*/ return null; }