-3

I am trying to implement a function something like this using python c api:

def get_add(x):
    def add(y):
        return x + y
    return add

add5 = get_add(5)
add10 = get_add(10)
add5(12) # 17
add10(12) # 22

module usage example:

from mymodule import get_add

add5 = get_add(5)
add10 = get_add(10)
add5(12) # 17
add10(12) # 22

where mymodule is writte in c.

  • 1
    Please tell us what is your desired result and what is the current result of your provided code snippets or the issue. Walk us through what your code does step by step until we get to the your specific question/issue. That way you can also edit your Question's title to a more specific and directly answerable question. You may use [this checklist](https://codeblog.jonskeet.uk/2012/11/24/stack-overflow-question-checklist/) as your guide before publishing your question. – Alex Pappas Sep 02 '21 at 15:07

1 Answers1

1

I've never actually seen PyFunction_SetClosure used, but I would expect to use it for modifying an existing Python function object to change the closure variables. It probably isn't suitable for implementing a generating a new function with a closure from scratch, because that would not be implemented in terms of PyFunctionObject (since these are specifically intended to process Python bytecode etc).

What you're looking for is a custom C API Python type with a __call__ attribute. The constructor of that type sets up the closure, and the the call attribute implements your function add.

I'll sketch out the basic scheme but have no interest in generating a full working example.

First define the struct that'll be your C API object:

typedef struct {
   PyObject_HEAD
   int a; // I'm assuming you only want to add ints
} Adder;

Next define an __init__ function to generate that struct (this could also be the __new__ function but let's not worry too much about that right now...)

static int
Adder_init(Adder *self, PyObject *args, PyObject *kwds) {
    // parse args and kwds to get the argument "a"
    // ...

    self->a = a;
    return 0;  // success!
};

Next define the __call__ function that does the adding

static PyObject*
Adder_call(Adder *self, PyObject *args, PyObject *kwds) {
    // parse args and kwds to get the argument "b"
    // ...

    int result = self->a + b;
    return PyLong_FromLong(result);
};

With those two functions complete you need to define the type object. Again, this is only sketched out below:

static PyTypeObject MyObject_Type = {
    PyVarObject_HEAD_INIT(NULL, 0)
    // other fields as needed
    .tp_init = Adder_init,
    .tp_call = Added_call,
};

Finally expose this type to Python as get_add and you have something that behaves exactly like you want.

DavidW
  • 29,336
  • 6
  • 55
  • 86