Estoy 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.Módulo de extensión Python con número variable de argumentos
Leyendo acerca de PyArg_ParseTuple parece que tiene que saber cuántos para aceptar, algunos obligatorios y algunos opcionales pero todos con su propia variable. Esperaba que PyArg_UnpackTuple fuera capaz de manejar esto, pero parece que solo me da errores de bus cuando intento y lo uso en lo que parece ser el camino equivocado.
Como ejemplo, tome el siguiente código 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 repiten a lo largo, hypot(3,4,5)
, hypot([3,4,5])
, y hypot(*[3,4,5])
todos dan la misma respuesta.
El comienzo de mi función C se parece a esto
static PyObject *hypot_tb(PyObject *self, PyObject *args) {
// lots of code
// PyArg_ParseTuple or PyArg_UnpackTuple
}
Muchos piensa que yasar11732. Aquí para el siguiente tipo hay un módulo de extensión que funciona completamente (_toolboxmodule.c) que simplemente toma cualquier número o argumento entero y devuelve una lista formada por esos argumentos (con un nombre deficiente). Un juguete pero ilustra lo que se debe 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]
¿Por qué usted nos dice que usted está recibiendo los accidentes de/tener dificultad con el '' PyArg_ * funciones, y después nos muestras todo excepto cómo usas las funciones 'PyArg_ *'? –
Debe colocar sus ParseArguments dentro de una instrucción if para ver si hubo errores durante el análisis (devuelto nulo), hacer una limpieza y devolver nulo si hubo errores. De lo contrario, suprimirá los errores durante el análisis de los argumentos. – yasar
Sí, sí, tienes razón. Editaré la publicación. –