如何从Python / C创建lambda

时间:2013-10-23 17:58:53

标签: python python-c-api

我们正在研究一些Python / C-API代码,我们遇到了一个希望传递回调的方法。该方法将作为反馈的形式定期更新回调。事实证明,我们对定期反馈并不感兴趣。禁用方法默认反馈机制的唯一方法是将其传递给某种回调。

我们采用的技术是声明一个只返回None的模块级函数,即:

static PyObject*
donothing(PyObject* self, PyObject* args) {
    return Py_None;
    }

但是,当然,这个函数也需要在模块方法表中注册,即:

static PyMethodDef methods[] = {
    {"donothing", donothing, METH_VARARGS, "do nothing"},
     ...
    {NULL}
    };

然后,当我们去调用方法时,我们需要获取对此方法的引用,即:PyObject_GetAttrString(module_reference, "donothing").

所有这些感觉就像我们花太多时间旋转我们的车轮而什么都不做。然后它对我来说......似乎是 lambda x:无的完美用法。但是在花了一个小时使用Python / C-API文档后,我无法弄清楚如何创建lambda。

我看到页面http://docs.python.org/2/c-api/function.html上有对闭包的引用,但我无法理清有关如何创建闭包的详细信息。

非常感谢任何指针(或对RTFM的引用)。

1 个答案:

答案 0 :(得分:3)

lambda表达式用于创建简单的匿名函数。它们有PyFunction_Type包装PyCode_Type的对象,这是一块可执行代码。但是你已经在C方面,所以创建一个Python函数会有点太多。相反,你应该创建一个PyCFunction_Type的对象。这类似于您尝试使用模块方法。

C中的样板也不会太大,但只有几行:

static PyObject *
donothing(PyObject *self, PyObject *args) {
    Py_RETURN_NONE;
}
static PyMethodDef donothing_ml = {"donothing", donothing, METH_VARARGS, "doc"};

然后使用PyCFunction_New(&donothing_ml, NULL)创建对象,产生<built-in function donothing>。此功能独立于您的模块,可以像任何其他PyObject一样使用。

它不是一个高级lambda,而是lambda *args: None的低级实现。

但是,如果您真的想要创建一个高级别lambda,则可以使用dastrobu建议的单一语句来执行此操作

l = PyRun_String("lambda *args: None", Py_eval_input, PyEval_GetGlobals(), NULL);

或者如果你想自己组装它,你可以做到

PyCodeObject *c = (PyCodeObject *) Py_CompileString("None", "fn", Py_eval_input);
#if PY_MAJOR_VERSION >= 3
c->co_name = PyUnicode_FromString("<c-lambda>"); // function name
#else
c->co_name = PyString_FromString("<c-lambda>"); // function name
#endif
c->co_flags |= CO_VARARGS; // accept *args
c->co_nlocals = 1; // needed in Python 3
l = PyFunction_New((PyObject *) c, PyEval_GetGlobals());

在这两种情况下,您都会获得一个函数,其中包含与dis(l)相当的分散代码lambda

  1           0 LOAD_CONST               0 (None)
              3 RETURN_VALUE