2011-11-03 17 views
10

Sto cercando di capire come nei moduli di estensione C avere un numero variabile (e forse) piuttosto elevato di argomenti per una funzione.Modulo di estensione Python con numero variabile di argomenti

Leggere circa PyArg_ParseTuple sembra che tu debba sapere quanti da accettare, alcuni obbligatori e alcuni facoltativi ma tutti con una propria variabile. Speravo che PyArg_UnpackTuple fosse in grado di gestirlo ma sembra che mi dia solo errori di bus quando cerco di usarlo in quello che sembra essere il modo sbagliato.

Come esempio, prendi il seguente codice python che potresti voler creare in un modulo di estensione (in 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]))) 

Questo può essere chiamato con un numero qualsiasi di argomenti o di ripetuti su, hypot(3,4,5), hypot([3,4,5]) e hypot(*[3,4,5]) tutti danno la stessa risposta.

L'inizio della mia funzione C si presenta così

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

Molti pensa a yasar11732. Qui per il prossimo ragazzo c'è un modulo di estensione completamente funzionante (_toolboxmodule.c) che accetta semplicemente qualsiasi argomento numerico o intero e restituisce una lista composta da quegli argomenti (con un nome povero). Un giocattolo ma illustra ciò che doveva essere fatto.

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

in Python, allora è:

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

Perché ci dite che state ottenendo gli arresti da/difficoltà con il '' PyArg_ * funzioni, e poi ci dimostrate tutto tranne come usi le funzioni 'PyArg_ *'? –

+0

Dovresti mettere i tuoi ParseArguments all'interno di un'istruzione if per vedere se ci sono stati errori durante l'analisi (restituito null), e fare una pulizia e restituire null se ci sono stati errori. Altrimenti sopprimerai gli errori durante l'analisi degli argomenti. – yasar

+0

Sì, sì hai ragione. Modifico il post –

risposta

8

avevo usato qualcosa di simile in precedenza. Potrebbe essere un brutto codice perché non sono un programmatore esperto di C, ma ha funzionato per me. L'idea è, * args è solo una tupla Python, e puoi fare qualsiasi cosa tu possa fare con una tupla Python. È possibile controllare 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; 
} 

stavo chiamando questa funzione come questa:

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

"Potrebbe essere un codice errato": il codice mi sta bene. –

+1

Wow, sono contento di averlo chiesto; l'hai inchiodato Un piccolo ritocco e il mio problema è stato risolto. Modificherò la mia domanda con la mia soluzione pienamente funzionante basata sul tuo per il prossimo ragazzo. –

Problemi correlati