2011-11-03 13 views
10

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] 
+0

¿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_ *'? –

+0

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

+0

Sí, sí, tienes razón. Editaré la publicación. –

Respuesta

8

que había usado algo como esto antes. Podría ser un código malo ya que no soy un codificador de C con experiencia, pero funcionó para mí. La idea es que * args es solo una tupla de Python, y puedes hacer cualquier cosa que puedas hacer con una tupla de Python. Puede marcar 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<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); 
     if (arr[i] == 0) { 
      PyErr_SetString(PyExc_ValueError,"Zero doesn't allowed as argument."); 
      return NULL; 
     } 
     if (PyErr_Occurred()) {return NULL; } 
    } 

    return 1; 
} 

estaba llamando esta función como esta:

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

"Podría ser un código incorrecto": el código me parece bien. –

+1

Wow, estoy contento de haber preguntado; lo lograste. Un pequeño ajuste y mi problema fue resuelto. Editaré mi pregunta con mi solución totalmente operativa basada en la tuya para el próximo tipo. –

Cuestiones relacionadas