Robert Franke 18.02.2010 1 Python erweitern in C und C++ Robert Franke DESY Zeuthen und Humboldt Universität Berlin Berlin, 18.02.2010
Robert Franke 18.02.2010 2 Inhalt 1 Einführung 2 Ctypes 3 Python API 4 Cython 5 SWIG 6 Boost.Python
Robert Franke 18.02.2010 3 Warum C/C++? Einführung Performance Wrappen existierender Bibliotheken Verteilen von proprietärem Code
Robert Franke 18.02.2010 4 Was ist ctypes Ctypes einfachstes Interface zu C-Bibliotheken in Standard Library seit Python 2.5 Basiert auf libffi für call conventions keine Änderung/Ergänzung der C-Bibliothek nötig! Automatische Typumwandlung von Argumenten Unterstützt Pointer, Structs, Unions Unterstützt Mac OSX, Linux, Windows
Ctypes Robert Franke 18.02.2010 5 Wrappen von sqrt aus libm.so import ctypes import math libm = ctypes.cdll.loadlibrary( libm.so ) sqrt = libm.sqrt sqrt.restype = ctypes.c_double sqrt.argtypes = [ctypes.c_double] print sqrt(3.2), math.sqrt(3.2)
Ctypes Robert Franke 18.02.2010 6 Arbeiten mit Structs #include <stdlib.h> struct doublearray{ unsigned long long length; double *array; ; double sum(struct doublearray* arr){ double s = 0.; unsigned long long i; for (i=0; i < arr->length; i++){ s += arr->array[i]; return s;
Ctypes Robert Franke 18.02.2010 7 import ctypes import random class DOUBLEARRAY(ctypes.Structure): _fields_ = [("length",ctypes.c_ulonglong), ("array",ctypes.pointer(ctypes.c_double))] libdoublearray=ctypes.cdll.loadlibrary("./doublearray.so") libdoublearray.sum.argtypes = [ctypes.pointer(doublearray)] libdoublearray.sum.restype = ctypes.c_double doublearray = ctypes.c_double * 100 array = doublearray() for i in xrange(0,100): array[i] = random.random() carray = DOUBLEARRAY(100, array) print libdoublearray.sum(carray)
Robert Franke 18.02.2010 8 Die Python API Python API Low-Level Interface Manuelles Reference Counting Wenig Overhead Viele Beispiele in der Standard Library
Python API Robert Franke 18.02.2010 9 Kubikwurzel #include <Python.h> #include <math.h> static PyObject* ErrorObject; static PyObject* cubic_root(pyobject* self, PyObject* args){ double guess = 1.0; double N; if (!PyArg_ParseTuple(args, "d", &N)) return 0; while (fabs((guess * guess * guess - N)/N) > 1e-8){ guess = 1./3 * (2 * guess + N/(guess * guess)); return Py_BuildValue("d",guess);
Python API Robert Franke 18.02.2010 10 Kubikwurzel (2) static char cubic_root doc [] = "Approximate the cubic root with the Babylonian method"; static PyMethodDef cubicmethods[] = { {"cubic_root", cubic_root, METH_VARARGS, cubic_root doc, {NULL, NULL, 0, NULL ; void initcubic(void){ PyObject *m, *d; m = Py_InitModule("cubic", cubicmethods); d = PyModule_GetDict(m); ErrorObject = Py_BuildValue("s", "cubic module error"); PyDict_SetItemString(d, "error", ErrorObject); if (PyErr_Occurred()) Py_FatalError("Can t initialize module cubic!");
Python API Robert Franke 18.02.2010 11 setup.py from distutils.core import setup, Extension setup(name = "cubic", version = "1.0", maintainer = "Robert Franke", maintainer_email = "robert.franke@desy.de", description = "Sample, simple Python extension module", ext_modules = [Extension( cubic,sources=[ cubic.c ])] )
Python API Robert Franke 18.02.2010 12 Eine Liste von Zufallszahlen #include <Python.h> #include <stdlib.h> #include <time.h> static PyObject* ErrorObject; static PyObject* random_list(pyobject* self, PyObject* args){ long nrand,i; double d; if (!PyArg_ParseTuple(args, "l", &nrand)) return 0; PyObject *list = PyList_New(nrand); for (i = 0; i < nrand; i++){ d = drand48(); PyObject *listitem = Py_BuildValue("d",d); PyList_SET_ITEM(list,i,listitem); return list; static PyObject* wrap_drand48(pyobject* self, PyObject* args){ double d; d = drand48(); return Py_BuildValue("d",d);
Python API Robert Franke 18.02.2010 13 Eine Liste von Zufallszahlen (2) static char wrap_drand doc [] = "A wrapper around the libc drand48 function"; static char random_list doc [] = "Generate a list of random numbers"; static PyMethodDef randommethods[] = { {"random", wrap_drand48, METH_VARARGS, wrap_drand doc, {"random_list", random_list, METH_VARARGS, random_list doc, {NULL, NULL, 0, NULL ; void initmyrandom(void){ PyObject *m, *d; m = Py_InitModule("myrandom", randommethods); d = PyModule_GetDict(m); ErrorObject = Py_BuildValue("s", "myrandom module error"); PyDict_SetItemString(d, "error", ErrorObject); if (PyErr_Occurred()) Py_FatalError("Can t initialize module myrandom!"); srand48(time(0));
Robert Franke 18.02.2010 14 Reference counting Python API Python nutzt reference counting zum Speichermanagement Owned reference vs. borrowed reference Py_INCREF: erhöht reference count Py_DECREF: erniedrigt reference count, löscht Objekt wenn count == 0 die meisten API Funktionen rufen INCREF für die Objekt, die sie zurückgeben Ausnamhme u.a.: PyListi_GetItem, PyTuple_GetItem Quelle von Crashs und Memory Leaks!
Python API Robert Franke 18.02.2010 15 Shallow list copy #include <Python.h> #include <stdlib.h> static PyObject* ErrorObject; static PyObject* copy_list(pyobject* self, PyObject* args){ Py_ssize_t len, i; PyObject *list; PyArg_ParseTuple(args,"O",&list); if (!PyList_Check(list)){ PyErr_SetString(PyExc_TypeError, "expected list type"); return NULL; len = PyList_Size(list); PyObject *new_list = PyList_New(len); for (i = 0; i < len; i++){ PyObject *listitem = PyList_GetItem(list, i); Py_INCREF(listitem); //Important as PyList_GetItem does not //increment the refcount! PyList_SET_ITEM(new_list,i,listitem); return new_list;
Python API Robert Franke 18.02.2010 16 Shallow list copy (2) static char copy_list doc [] = "Make a shallow list copy"; static PyMethodDef copy_listmethods[] = { {"copy", copy_list, METH_VARARGS, copy_list doc, {NULL, NULL, 0, NULL ; void initcopy_list(void){ PyObject *m, *d; m = Py_InitModule("copy_list", copy_listmethods); d = PyModule_GetDict(m); ErrorObject = Py_BuildValue("s", "copy_list module error"); PyDict_SetItemString(d, "error", ErrorObject); if (PyErr_Occurred()) Py_FatalError("Can t initialize module copy_list!");
Robert Franke 18.02.2010 17 Was ist Cython? Cython Python mit Typen vorher: Pyrex Methoden werden mit Typdeklarationen angereichert Cython macht daraus C-Code, den man dann kompiliert
Cython Robert Franke 18.02.2010 18 Kubikwurzel from math import fabs def cubic_root(double f): cdef double x = 1.5 # Start value while fabs((x * x * x - f)/f) > 1e-8: x = (2 * x + f/(x * x))/3. return x
Robert Franke 18.02.2010 19 Was ist SWIG? SWIG Simplified Wrapper and Interface Generator Wrappergenerator für viele Sprachen: Python, Perl, Ruby, TCL, Common List, C#, etc. Unterstützt C und C++ Definitionen in Interface-Files Oft können Header direkt als Interface Deklarationen benutzt werden
SWIG Robert Franke 18.02.2010 20 Kubikwurzel %module cubic %{ extern double cubic_root(double); % extern double cubic_root(double);
Boost.Python Was ist Boost.Python Robert Franke 18.02.2010 21 Teil der Boost C++-Bibliothek Entwickelt von Dave Abrahams et al. Aktuell: Boost 1.42.0, Version 2 von Boost.Python Unterstützt Funktionen, Memberfunktionen, Klassenfunktionen, Docstrings, Properties, Overloading, Keywordargumente, Defaultargumente, Serialization etc. Macht starken Gebrauch von Template Metaprogramming
Boost.Python Robert Franke 18.02.2010 22 Kubikwurzel #include <boost/python.hpp> #include <cmath> using namespace boost::python; double cubic_root(const double N){ double guess = 1.0; while (std::fabs((guess * guess * guess - N)/N) > 1e-8){ guess = 1./3 * (2 * guess + N/(guess * guess)); return guess; BOOST_PYTHON_MODULE(cubic){ def("cubic_root",&cubic_root);
Boost.Python Robert Franke 18.02.2010 23 Ein Wrapper um boost::gil #include <stdexcept> #include <boost/filesystem.hpp> #include "image.h" using namespace boost::gil; using namespace boost::filesystem; using namespace std; using boost::shared_ptr; Image::Image(const std::string& path){ if (!exists(path)){ throw std::runtime_error("file does not exist"); jpeg_read_image(path,img_); img_view_ = view(img_); shared_ptr<pixel> Image::at(const int x, const int y) const{ rgb8c_view_t::iterator it = img_view_.at(x,y); Pixel* px = new Pixel((*it)[0],(*it)[1],(*it)[2]); return shared_ptr<pixel>(px); unsigned int Image::width() const{ return static_cast<unsigned int>(img_view_.width()); unsigned int Image::height() const{ return static_cast<unsigned int>(img_view_.height()); // includes all needed Boost.Filesystem declarations
Boost.Python Robert Franke 18.02.2010 24 Ein Wrapper um boost::gil (2) #include <boost/python.hpp> #include <string> #include "image.h" using namespace boost::python; using boost::shared_ptr; using std::string; BOOST_PYTHON_MODULE(pyimage){ class_<pixel, shared_ptr<pixel> >("Pixel",init<short,short,short>()).def_readwrite("red",&Pixel::r_).def_readwrite("green",&Pixel::g_).def_readwrite("blue",&Pixel::b_) ; class_<image>("image",init<string>()).def("width",&image::width).def("height",&image::height).def("at",&image::at) ;
Boost.Python Robert Franke 18.02.2010 25 Callbacks in C++ und Python #include "callback.h" #include <boost/foreach.hpp> double mapsum(const std::vector<double>& vec, map_func_type func){ double sum = 0; BOOST_FOREACH(double d, vec){ sum += func(d); return sum; double square(const double d){ return d * d;
Boost.Python Robert Franke 18.02.2010 26 Callbacks in C++ und Python (2) #include <boost/python.hpp> #include <boost/python/suite/indexing/vector_indexing_suite.hpp> #include "callback.h" #include "py_boost_function.hpp" #include "container_conversions.h" map_func_type square_ptr(square); using namespace boost::python; using namespace scitbx::boost_python::container_conversions; BOOST_PYTHON_MODULE(callback){ boost::python::class_<std::vector<double>, boost::shared_ptr<std::vector<double> > >("PyVec").def(boost::python::vector_indexing_suite<std::vector<double> >()); from_python_sequence<std::vector<double>,variable_capacity_policy>(); def("reduce",&mapsum); def_function<double (const double)>( "map_func_t", "A function" ); scope().attr("square") = square_ptr;
Boost.Python Dokumentation und Links Robert Franke 18.02.2010 27 http://docs.python.org/c-api/ http://docs.python.org/library/ctypes.html http://www.swig.org/doc.html http://www.boost.org/doc/libs/1_42_0/libs/ python/doc/index.html http://wiki.python.org/moin/boost.python/howto http://edcjones.tripod.com/refcount.html