C-API: Pass a tuple to a method of a class

Stefan Behnel stefan_ml at behnel.de
Tue Jan 10 08:01:29 EST 2012


Hi again,

just as a little teaser, to make it clear that I'm not joking, here's your
code below translated into Cython.

Stefan Behnel, 10.01.2012 13:33:
> pasparis at noos.fr, 10.01.2012 11:57:
>> the code is the following:
> [...]
>> // Class
>> pclass = PyObject_GetAttrString(mymod, "cVector");
>> if (pclass == NULL) {
>> Py_DECREF(pclass);
>> cout << "Can't find class\n";
>> }
>>
>> // Parameters/Values
>> args = Py_BuildValue("(f)", 100.0);
>> if (args == NULL) {
>> Py_DECREF(args);
>> cout << "Can't build argument list for class instance\n";
>> }
>>
>> // Object with parameter/value
>> object = PyEval_CallObject(pclass, args);
>> if (object == NULL) {
>> Py_DECREF(object);
>> cout << "Can't create object instance:\n";
>> }
>>
>> // Decrement the argument counter as we'll be using this again
>> Py_DECREF(args);
>>
>> // Get the object method - note we use the object as the object
>> // from which we access the attribute by name, not the class
>> method = PyObject_GetAttrString(object, "ComputeNorm");
>> if (method == NULL) {
>> Py_DECREF(method);
>> cout << "Can't find method\n";
>> }
>>
>> // Decrement the counter for our object, since we now just need
>> // the method reference
>> Py_DECREF(object);
>>
>> // Build our argument list - an empty tuple because there aren't
>> // any arguments
>>
>> cout << "Prepare the Tuple:\n" ;
>> // WE pass a tuple
>> args = PyTuple_New( 3 );
>> if (args == NULL) {
>> Py_DECREF(args);
>> cout << "Can't build argument list for method call\n";
>> }
>>
>> PyObject *py_argument;
>> // 1st argument
>> py_argument = PyFloat_FromDouble(5.);
>> PyTuple_SetItem(args, 0, py_argument);
>>
>> // 2nd argument
>> py_argument = PyFloat_FromDouble(10.);
>> PyTuple_SetItem(args, 1, py_argument);
>>
>> // 3nd argument
>> py_argument = PyFloat_FromDouble(15.);
>> PyTuple_SetItem(args, 2, py_argument);
>>
>> cout << "Before the Exec:\n" ;
>> // Call our object method with arguments
>> //ret = PyEval_CallObject(method,args);
>> ret = PyObject_CallObject(method,args);
> 
> Note that you are calling the method with three arguments here. It appears
> that what you want is *one* argument instead, which happens to be a tuple.
> So you need to wrap it in another tuple for calling. As I said, Cython will
> do that for you.

And here's the Cython code:

"""
# in module "gluecode.pyx" (or whatever you want to name it)

import mModule8

cdef api float compute_norm(float init_value, float x, float y, float z):
    vec = mModule8.cVector(init_value)
    return vec.ComputeNorm( (x,y,z) )
"""

At least, that's what I read from your C code above. I'm assuming here that
your program is using C or C++, and that you want to embed a CPython
runtime in it and be able to execute Python code through it. The above
"compute_norm()" function is exported (as a C function) by the "gluecode"
module which you can import in your C/C++ code (as you did already).

Note also that the Cython code above is substantially more efficient than
your implementation, because it uses faster type conversions and interned
Python names for looking up the class and its method.

Stefan




More information about the Python-list mailing list