Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create lambda's from Python/C

We're working on some Python/C-API code, and we've encountered a method that would like to be passed a callback. The method will ship periodic updates to the callback as a form a feedback. As it turns out, we're not that interested in periodic feedback. The only way to disable the method's default feedback mechanism is to pass it some kind of callback.

The technique we've employed is to declare a module level function that just returns None, ie:

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

But of course, this function also needs to be registered with the modules method table, ie:

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

Then, when we go to call the method, we need to grab a reference to this method, ie: PyObject_GetAttrString(module_reference, "donothing").

All of this feels like we're spending too much time spinning our wheels just to do nothing. Then it ocurred to me..hey, seems like a perfect use for lambda x: None. But after spending an hour with the Python/C-API docs, I can't figure out how one create's lambdas.

I see there are references to closures on the page http://docs.python.org/2/c-api/function.html, but I can't sort out the details on how one creates them.

Any pointers (or references to RTFM) would be greatly appreciated.

like image 756
user590028 Avatar asked Oct 03 '22 15:10

user590028


1 Answers

A lambda expressions is used to create simple anonymous functions. These have a PyFunction_Type wrapping an object of PyCode_Type, which is a chunk of executable code. But you're already on the C side, so creating a Python function would be a little too much. Instead you should create an object of PyCFunction_Type. This is similar to what you've tried to do with the module methods.

The boilerplate in C wouldn't be too big either, but only a few lines:

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

The object then is created with PyCFunction_New(&donothing_ml, NULL) which yields a <built-in function donothing>. This function is independent of your module and can be used like any other PyObject.

It's not exactly a high level lambda, but rather a low level implementation of lambda *args: None.


However if you'd really like to create a high level lambda you can do this with a single statement like dastrobu proposed

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

or if you'd like to assemble it yourself you could do

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());

In both cases you'll get a function with dissasembled code dis(l) equivalent to a lambda:

  1           0 LOAD_CONST               0 (None)
              3 RETURN_VALUE        
like image 52
tynn Avatar answered Oct 07 '22 17:10

tynn