如何在 c 扩展模块中创建和 return 函数对象?
How to create and return a function object in a c extension module?
我正在尝试编写一个 python 扩展模块,其中一些函数已被柯里化,但我不太确定如何去做。主要困难在于我不确定如何创建和 return 一个 PyFunction 对象,以及如何将其参数的解析规则传递给它。有没有一种相当有效的方法来做到这一点,或者这是精神错乱?
从 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
的类型。据我所知,目前还没有这样的东西。
我正在尝试编写一个 python 扩展模块,其中一些函数已被柯里化,但我不太确定如何去做。主要困难在于我不确定如何创建和 return 一个 PyFunction 对象,以及如何将其参数的解析规则传递给它。有没有一种相当有效的方法来做到这一点,或者这是精神错乱?
从 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
的类型。据我所知,目前还没有这样的东西。