我想寫一個python擴展模塊,其中的一些功能是curried,但我不太確定如何去做這件事。主要困難在於我不確定如何創建並返回一個PyFunction對象,以及如何將其參數的解析規則傳遞給它。有沒有一個相當有效的方法來做到這一點,或者是這種瘋狂?如何在c擴展模塊中創建並返回一個函數對象?
從Python方所需的語義是:
# given a function f(x, y)
f(a, b) -> result
f(a) -> f'
f'(b) -> result
我想寫一個python擴展模塊,其中的一些功能是curried,但我不太確定如何去做這件事。主要困難在於我不確定如何創建並返回一個PyFunction對象,以及如何將其參數的解析規則傳遞給它。有沒有一個相當有效的方法來做到這一點,或者是這種瘋狂?如何在c擴展模塊中創建並返回一個函數對象?
從Python方所需的語義是:
# given a function f(x, y)
f(a, b) -> result
f(a) -> f'
f'(b) -> result
讓我們來看看一個可能的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
的類型。據我所知,目前還沒有這樣的事情。