Python Bindingď
Any code that you write using any compiled language like C, C++ or Java can be integrated or imported into a Python script. This code is considered as an âextensionâ. A Python extension module is nothing more than a normal C library. On Unix machines, writting extensions, usually requires installing a developer-specific package such as python2.5-dev.
For your first look at a Python extension module, youâll be grouping your code into three parts:
The C functions you want to expose as the interface from your module.
A table mapping the names of your functions as Python developers will see them to C functions inside the extension module.
An initialization function.
The C functions:ď
The signatures of the C implementations of your functions will always take one of the following three forms:
static [[PyObject]] MyFunction( PyObjectself, PyObject *args );
static [[PyObject]] MyFunctionWithKeywords(PyObjectself, PyObject args, PyObjectkw);
static [[PyObject]] MyFunctionWithNoArgs( PyObjectself );
Each one of the preceding declarations returns a Python object. Thereâs no such thing as a void function in Python as there is in C. If you donât want your functions to return a value, return the C equivalent of Pythonâs None value. The Python headers define a macro, Py_RETURN_NONE, that does this for us.
The names of your C functions can be whatever you like as they will never be seen outside of the extension module. So they would be defined as static function.
Your C functions usually are named by combining the Python module and function names together, as shown here: static [[PyObject]] * [[FileName]] _isImage(PyObject obj, PyObjectargs, PyObject *kwargs) { if (isImage(FileName _Value(obj))) Py_RETURN_TRUE; else Py_RETURN_FALSE; }
This would be a Python function called isImage inside of the module [[FileName]]. Youâll be putting pointers to your C functions into the method table for the module that usually comes next in your source code.
The method mapping tableď
This method table is a simple array of [[PyMethodDef]] structures. That structure looks something like this: struct [[PyMethodDef]] { char ml_name; [[PyCFunction]] ml_meth; int ml_flags; charml_doc; };
Here is the description of the members of this structure:
ml_name:
This is the name of the function as the Python interpreter
will present it when it is used in Python programs.
ml_meth:
This must be the address to a function that has any one of
the signatures described in previous seection.
ml_flags:
This tells the interpreter which of the three signatures
ml_meth is using.
This flag will usually have a value of METH_VARARGS.
This flag can be bitwise orâed with METH_KEYWORDS if you want to allow keyword arguments into your function.
This can also have a value of METH_NOARGS that indicates you donât want to accept any arguments.
ml_doc:
This is the docstring for the function, which could be NULL
if you donât feel like writing one
This table needs to be terminated with a sentinel that consists of NULL and 0 values for the appropriate members.
Example:
static[[PyMethodDef]][[FileName]]_methods[] = { { âcomposeâ, (PyCFunction) FileName_compose, METH_VARARGS, âCompose from root, number and extension OR prefix with number @â }, { âcomposeBlockâ, (PyCFunction) FileName_composeBlock, METH_VARARGS, âCompose from blockname, number, root and extensionâ }, { NULL } /* Sentinel */ };
The initialization functionď
The last part of your extension module is the initialization function.
This function is called by the Python interpreter when the module is
loaded. Itâs required that the function be namedinitModule
, where
Module is the name of the module (the name isinitxmipp
in our
case).
Your C initialization function generally has the following overall structure:
[[PyMODINIT]]FUNC initModule() { Py[InitModule3]; }
Here is the description of Py_InitModule3 function:
func:
This is the function to be exported.
module_methods:
This is the mapping table name defined above.
docstring:
This is the comment you want to give in your extension.
Example:
[[PyMODINIT]]_FUNC initxmipp(void) { //Initialize module variable[[PyObject]]* module; module = Py_InitModule3(âxmippâ, xmipp_methods, âXmipp module as a Python extension.â);
⌠}
All togetherď
A simple example that makes use of all the above concepts:
#include <Python.h>
static[[PyObject]]* helloworld(PyObject* self) { return Py_BuildValue(âsâ, âHello, Python extensions!!â); }
static char helloworld_docs[] = âhelloworld( ): Any message you want to put here!!:raw-latex:nâ;
static[[PyMethodDef]] helloworld_funcs[] = { {âhelloworldâ, (PyCFunction)helloworld, METH_NOARGS, helloworld_docs}, {NULL} };
void inithelloworld(void) { Py_InitModule3(âhelloworldâ, helloworld_funcs, âExtension module example!â); }
Passing Function parametersď
Most of the time you will add functions to an existing module. For example, the following function, that accepts some number of parameters, would be defined like this:
static[[PyObject]]module_func(PyObject self, PyObjectargs) { / Parse args and do something interesting here. */ Py_RETURN_NONE; }
The method table containing an entry for the new function would look like this:
static[[PyMethodDef]] module_methods[] = {
{ âfuncâ, module_func, METH_VARARGS, âhelp messageâ }, { NULL, NULL, 0, NULL } };
You can use API[[PyArg]]_ParseTuple
function to extract the
arguments from the one[[PyObject]] pointer passed into your C function.
The first argument to[[PyArg]]_ParseTuple
is the args argument.
This is the object youâll be parsing. The second argument is a format
string describing the arguments as you expect them to appear. Each
argument is represented by one or more characters in the format string
as follows.
static[[PyObject]] module_func(PyObjectself, PyObject args) { int i; double d; chars;
if (!PyArg_ParseTuple(args, âidsâ, &i, &d, &s)) { return NULL; }
/* Do something interesting here. */ Py_RETURN_NONE; }
Compiling the new version of your module and importing it will enable you to invoke the new function with any number of arguments of any type:
The PyArg _ParseTuple Functionď
Here is a list of format codes for[[PyArg]] _ParseTuple function:
Code | C type | Meaning |
c | char | A Python string of length 1 becomes a C char. |
d | double | A Python float becomes a C double. |
f | float | A Python float becomes a C float. |
i | int | A Python int becomes a C int. |
l | long | A Python int becomes a C long. |
L | long long | A Python int becomes a C long long |
O | [[PyObject]]* | Gets non-NULL borrowed reference to Python argument. |
s | char* | Python string without embedded nulls to C char*. |
s# | char*+int | Any Python string to C address and length. |
t# | char*+int | Read-only single-segment buffer to C address and length. |
u | Py_UNICODE* | Python Unicode without embedded nulls to C. |
u# | Py_UNICODE*+int | Any Python Unicode C address and length. |
w# | char*+int | Read/write single-segment buffer to C address and length. |
z | char* | Like s, also accepts None (sets C char* to NULL). |
z# | char*+int | Like s#, also accepts None (sets C char* to NULL). |
(âŚ) | as per ⌠| A Python sequence is treated as one argument per item. |
The following arguments are optional. | ||
: | Format end, followed by function name for error messages. | |
; | Format end, followed by entire error message text. |
Returning Values:ď
Py_BuildValue takes in a format string much like PyArg _ParseTuple does. Instead of passing in the addresses of the values youâre building, you pass in the actual values. Hereâs an example showing how to implement an add function:
static[[PyObject]] foo_add(PyObjectself, PyObject *args) { int a; int b;
if (!PyArg_ParseTuple(args, âiiâ, &a, &b)) { return NULL; } return Py_BuildValue(âiâ, a + b); }
This is what it would look like if implemented in Python:
You can return two values from your function as follows, this would be cauptured using a list in Python.
static[[PyObject]] foo_add_subtract(PyObjectself, PyObject *args) { int a; int b;
if (!PyArg_ParseTuple(args, âiiâ, &a, &b)) { return NULL; } return Py_BuildValue(âiiâ, a + b, a - b); }
This is what it would look like if implemented in Python:
Calling Python (+numpy) from Cď
Here is an example code to perform the sum of two volumes in Python:
#include <data/xmipp_image.h>
#include <Python.h> #include <numpy/ndarrayobject.h>
void myImport_array() { import_array(); }
int main() { try { time_config();
Image I; I.read(â/home/coss/temp/BPV_Project/BPV_scale_filtered_windowed.volâ);[[ProcessorTimeStamp]] t0; const[[MultidimArray]] &mI=I(); annotate_processor_time(&t0); double retval=0.0; FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(mI) retval+=DIRECT_MULTIDIM_ELEM(mI,n)+DIRECT_MULTIDIM_ELEM(mI,n); std::cout << elapsed_time(t0,false) << std::endl; std::cout << âIn C++:â << retval << std::endl;
std::cout << âInitializing Python:raw-latex:nâ; annotate_processor_time(&t0); Py_Initialize(); myImport_array(); std::cout << elapsed_time(t0,false) << std::endl;
// Create numpy array in Python with I() std::cout << âCreating numpy array:raw-latex:nâ; annotate_processor_time(&t0); npy_intp dim[3]; dim[0]=ZSIZE(I()); dim[1]=YSIZE(I()); dim[2]=XSIZE(I());[[PyObject]] pyI=PyArray_SimpleNewFromData(3, dim, NPY_DOUBLE, (void)MULTIDIM_ARRAY(I())); std::cout << elapsed_time(t0,false) << std::endl;
// Import testPython std::cout << âImporting module:raw-latex:nâ; annotate_processor_time(&t0);[[PyObject]]* pName =[[PyString]]_FromString(âtestPythonâ); // Import testPython PyObject* pModule = PyImport_Import(pName); Py_DECREF(pName); std::cout << elapsed_time(t0,false) << std::endl;
// Call sum std::cout << âCalling sum:raw-latex:nâ; annotate_processor_time(&t0); [[PyObject]] arglist = Py_BuildValue(âOOâ, pyI, pyI); PyObjectpFunc = PyObject_GetAttrString(pModule, âsumâ); PyObject *result = PyObject_CallObject(pFunc, arglist); std::cout << elapsed_time(t0,false) << std::endl; std::cout << âIn Python:â << PyFloat_AsDouble(result) << std::endl; } catch (XmippError e) { std::cout << e << std::endl; } return 0; }
You have to compile with
xmipp_compile -i myCode.cpp âpython
And the Python code is