5

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

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

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

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

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

那么,当我们去调用该方法时,我们需要获取该方法的一个引用,即:PyObject_GetAttrString(module_reference, "donothing").

所有这一切感觉就像我们花太多时间转动我们的轮子只是为了无所事事。然后它发生在我身上..嘿,似乎是lambda x: None的完美用途。但是在使用 Python/C-API 文档花了一个小时之后,我无法弄清楚如何创建 lambdas。

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

任何指针(或对 RTFM 的引用)将不胜感激。

4

1 回答 1

4

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.


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

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)等效于 a的函数lambda

  1           0 LOAD_CONST               0 (None)
              3 RETURN_VALUE        
于 2015-08-01T16:30:28.480 回答