0

我想寫一個python擴展模塊,其中的一些功能是curried,但我不太確定如何去做這件事。主要困難在於我不確定如何創建並返回一個PyFunction對象,以及如何將其參數的解析規則傳遞給它。有沒有一個相當有效的方法來做到這一點,或者是這種瘋狂?如何在c擴展模塊中創建並返回一個函數對象?

從Python方所需的語義是:

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

回答

1

讓我們來看看一個可能的Python實現第一。

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

這就需要用C做此以某種方式創建lambda功能的唯一的事。在這裏,我們不知道調用C函數本身的PyCFunction的問題。所以我們必須編寫一個包裝器並創建一個新的PyCFunction對象。

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

這產生

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

在這裏,我們濫用PyCFunction類型一點並傳遞給PyCFunction_New(&curried_def, args)第二參數所需的語義被認爲是該功能被綁定到self對象,因此我們將會得到一個元組對象的內置方法,其元組對象爲。如果您需要原始函數的self參數或使用關鍵字參數,則必須稍微擴展一下這個hack,然後構建一個自定義對象來代替args。也可以爲咖喱功能創建一個類似PyCFunction的類型。據我所知,目前還沒有這樣的事情。

相關問題