【问题标题】:How to create lambda's from Python/C如何从 Python/C 创建 lambda
【发布时间】:2013-10-23 17:58:53
【问题描述】:

我们正在编写一些 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 文档之后,我无法弄清楚如何创建 lambda。

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

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

【问题讨论】:

  • 你能不能用builtins.id来代替你自己的do-nothing函数?
  • 在这个链接(secnetix.de/olli/Python/lambda_functions.hawk)你可以找到更多的lambda例子。
  • 使用 builtins.id() 的想法可以避免我们用愚蠢的 donothing() 方法污染我们的模块(这就是我投票赞成它的原因)......但我真的希望获得一些关于使用 Python/C-API 创建 lambda 的指示。
  • 你为什么不使用类似的东西:f = PyRun_String("lambda x: None", Py_single_input, globals, locals);。我猜函数对象的创建没有时间限制。从 C 语言评估 python 代码有时是最简单的解决方案(也是最易读的)。
  • 这真是个好主意。我浏览了有关此方法的文档,发现 Py_CompileStringFlags()。这实际上可能是一种假设创建 CodeObjects 的方式,然后将它们关联为一种类型的任意属性。感谢您的提示!

标签: python python-c-api


【解决方案1】:

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        

【讨论】:

    猜你喜欢
    • 2020-05-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-17
    • 1970-01-01
    • 1970-01-01
    • 2021-04-30
    相关资源
    最近更新 更多