2011-11-03 4 views
10

Я пытаюсь выяснить, как в модулях расширения C иметь переменную (и, возможно,) довольно большое количество аргументов функции.Модуль расширения Python с переменным числом аргументов

Чтение о PyArg_ParseTuple Кажется, вы должны знать, сколько принять, некоторые обязательные и некоторые необязательные, но все с их собственной переменной. Я надеялся, что PyArg_UnpackTuple сможет справиться с этим, но, похоже, мне просто дают ошибки шины, когда я пытаюсь использовать его в том, что кажется неправильным.

В качестве примера возьмем следующий код python, который можно сделать в модуль расширения (на 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]))) 

Это может быть вызвана с любым числом аргументов или итерации, hypot(3,4,5), hypot([3,4,5]) и hypot(*[3,4,5]) все дают один и тот же ответ.

Начало моей функции C выглядит следующим образом

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

Многие думают yasar11732. Здесь для следующего парня есть полностью работающий модуль расширения (_toolboxmodule.c), который просто принимает любые аргументы числа или целого и возвращает список, состоящий из этих аргументов (с плохим именем). Игрушка, но иллюстрирует, что нужно сделать.

#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"); 
} 

В питоне, то это:

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

Почему вы сказать нам, что вы получаете от аварии/возникли трудности с '' PyArg_ * функции, а затем показать нам все, кроме как вы используете функции 'PyArg_ *'? –

+0

Вы должны поместить свои ParseArguments внутри оператора if, чтобы увидеть, были ли ошибки во время разбора (возвращены null), и выполнить очистку и вернуть null, если были ошибки. В противном случае вы будете подавлять ошибки во время разбора аргументов. – yasar

+0

Да, да, вы правы. Я отредактирую сообщение. –

ответ

8

я что-то вроде этого раньше использовали. Это может быть плохой код, поскольку я не опытный C-кодер, но он работал для меня. Идея такова: * args - всего лишь кортеж Python, и вы можете делать все, что вы можете сделать с кортежем Python. Вы можете проверить 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; 
} 

Я звала эту функцию так:

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

«Это может быть плохой код»: код выглядит хорошо для меня. –

+1

Вау, я рад, что спросил; ты сделал это. Небольшая настройка и моя проблема была решена. Я отредактирую свой вопрос с моим полностью работающим решением, основанным на вашем, для следующего парня. –