0

Ich versuche, ein Python-Erweiterungsmodul zu schreiben, wo einige der Funktionen curried sind, aber ich bin nicht ganz sicher, wie man das macht. Die Hauptschwierigkeit besteht darin, dass ich nicht sicher bin, wie man ein PyFunction-Objekt erzeugt und zurückgibt und wie man es dann den Parse-Regeln für seine Argumente übergibt. Gibt es einen ziemlich effizienten Weg, dies zu tun oder ist dieser Wahnsinn?Wie erstellt und gibt ein Funktionsobjekt in einem c-Erweiterungsmodul zurück?

Von der Python-Seite die gewünschte Semantik wäre:

# given a function f(x, y) 
f(a, b) -> result 
f(a) -> f' 
f'(b) -> result 

Antwort

1

Lassen Sie uns zunächst auf eine mögliche Python-Implementierung einen Blick.

def f(x, y=None): 
    if y is None: 
     return lambda y: f(x, y) 
    return 'result' 

Das einzige, was hier in C getan werden muss, ist irgendwie die lambda Funktion zu schaffen. Hier wissen wir nicht, dass die PyCFunction die C-Funktion selbst aufruft. Also müssen wir Wrapper darum schreiben und ein neues PyCFunction Objekt erstellen.

static PyObject* curried (PyObject *old_args, PyObject *new_args); 
static PyMethodDef curried_def = {"curried", curried, METH_VARARGS, "curried"}; 

static PyObject* f (PyObject *self, PyObject *args) { 
    PyObject *x = NULL, *y = NULL; 
    if(!PyArg_ParseTuple(args, "O|O", &x, &y)) 
     return NULL; 

    // validate x 
    if (y == NULL) 
     return Py_INCREF(args), PyCFunction_New(&curried_def, args); 
    // validate y 

    // do something to obtain the result 
    return result; 
} 

static PyObject* curried (PyObject *old_args, PyObject *new_args) { 
    Py_ssize_t old_args_count = PyTuple_Size(old_args); 
    Py_ssize_t new_args_count = PyTuple_Size(new_args); 
    PyObject *all_args = PyTuple_New(old_args_count + new_args_count); 
    Py_ssize_t i; 
    PyObject *o; 
    for (i = 0; i < old_args_count; i++) { 
     o = PyTuple_GET_ITEM(old_args, i); 
     Py_INCREF(o); 
     PyTuple_SET_ITEM(all_args, i, o); 
    } 
    for (i = 0; i < new_args_count; i++) { 
     o = PyTuple_GET_ITEM(new_args, i); 
     Py_INCREF(o); 
     PyTuple_SET_ITEM(all_args, old_args_count + i, o); 
    } 
    return f(NULL, all_args); 
} 

dieser die gewünschte Semantik von

f(a, b) -> result 
f(a) -> <built-in method curried of tuple object at 0x123456> 
f(a)(b) -> result 

Hier ergibt mißbrauchen wir die PyCFunction Art ein wenig und den zweiten Parameter zu PyCFunction_New(&curried_def, args) geben soll das self Objekt sein, diese Funktion gebunden, wir daher Ich werde eine eingebaute Methode curried von Tuple-Objekt bekommen. Wenn Sie den Parameter self der ursprünglichen Funktion benötigen oder Schlüsselwortargumente verwenden, müssen Sie diesen Hack etwas erweitern und ein benutzerdefiniertes Objekt erstellen, das anstelle der args übergeben wird. Es ist auch möglich, einen Typ wie PyCFunction für Curry-Funktionen zu erstellen. Soweit ich weiß, gibt es so etwas noch nicht.