常见的python 代码加速方案中,直接书写 C 代码是最复杂的,必须要遵循 python 和 C 的接口方案;[pypy](http://pypy.org/) 适合纯 python 代码优化,需要更改的内容最少;[numba](https://numba.pydata.org/)适合做数值优化,开`nopython=True`是最快的,但是很多 python 的函数或者类型不支持,但是使用起来很简单;[cython](https://cython.org/) 自定义了一套 python+C的语法,最简单的使用C编写动态链接库,使用cython 作为接口。
以下是例子:[例子](https://equatorialmaths.wordpress.com/2010/10/16/python-extensions-with-c-libraries-made-easy-by-cython/)
## Python extensions with C libraries made easy by Cython
Suppose you have a library written in C that you like to be called from Python. There are many ways to accomplish this, and I would like to show here a complete example of doing this using [Cython](http://www.cython.org/), which is basically a compiler from (an extension of Python) to C. Most of this, and much more, can be found in a quick[tutorial](http://conference.scipy.org/proceedings/SciPy2009/paper_1/full_text.pdf).
For the purpose of an example, suppose you have a C function that computes the average of an array of doubles (all source files here, as well as the Makefile that basically contains the shell commands we show below, can be downloaded [here](https://github.com/dimpase/dyn/releases), or cloned using git from [github](https://github.com/dimpase/dyn)):
```
/* cmean.c */
double mean(int n, double* a)
{
double s;
int i;
for (s=0., i=0; i<n; i++) s+=*(a++);
return s/n;
}
```
with the prototype
```
/\* cmean.h \*/
double mean(int, double\*);
```
and you want to call it from Python, i.e. (we need to give the function another name here, cmean(), for technical as well as for semantic reasons—noone would pass the length of an array and the array to a function in Python, as the array itself would certainly do) say
```
b=[1.3,5.777777,-12.0,77.]
print cmean(b)
```
would print the same as
```
print sum(b)/len(b)
```
As cmean() is not a Python built-in, we would expect to import it first. So the following
```
$ python test.py
```
where
```
# test.py
import dyn
from dyn import cmean
b=[1.3,5.777777,-12.0,77.]
print cmean(b)
print sum(b)/len(b)
```
will print the number 18.01944425 twice. OK, so here is cmean() definition in Cython (note the file extension .pyx)
```
# m.pyx
cdef extern from "cmean.h":
double mean(int, double*)
from stdlib cimport *
def cmean(a):
n = len(a)
cdef double *v
v = malloc(n*sizeof(double))
for i in range(n):
v[i] = float(a[i])
m = mean(n, v)
free(v)
return m
```
The main task of cmean() is to create a “plain” C array of doubles, pass it to our C function, get the result, clean after itself, and return the result. Unlike Python, Cython has types declarations. Unless one calls C-function from the Cython code, they are not mandatory though, although they can speed the things up tremendously in “real” computations. In the example here
```
cdef double *v
```
cannot be avoided — the code does not compile without it. In a usual C-like fasion, instead of 2 lines
```
cdef double *v
v = malloc(n*sizeof(double))
```
one could have written just
```
cdef double *v = malloc(n*sizeof(double))
```
The top two lines pass the prototype declaration of the C-function mean to be included into the C file to be generated, and the third line imports the functions from the C library stdlib (we need malloc and free from there).
Having this, we need to create the extension module dyn, to be imported by Python. This is perhaps the least intuitive part of the job. One way to accomplish it is to use Python distutils: i.e. we need to create a setup.py file to be called as
```
$ python setup.py build_ext --inplace
```
This should look as follows:
```
# setup.py
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
ext_modules=[
Extension("dyn",
["m.pyx"],
library_dirs = ['.'],
libraries=["cmean"]) # Unix-like specific
]
setup(
name = "Demos",
cmdclass = {"build_ext": build_ext},
ext_modules = ext_modules
)
```
One not yet explained thing here is how the shared library containing the compiled C-function mean() is hooked up to dyn. You see in the code above the declarations library\_dirs and libraries. They assume that the shared library is called libcmean.so and that it is in the same directory (i.e. ‘.’ is the path to it) as the rest of the code. One can create libcmean.so (before creating dyn, more precisely, a file called dyn.so) by running
```
$ gcc -fPIC -shared cmean.c -o libcmean.so
```
One more point to watch is that on some Unix systems the loader will be unable to locate the dynamic libraries libcmean.so and/or dyn.so we created (unless they are placed in certain stanadard directories, which is not something one wants to do with experimental code!). So test.py will need to be run, e.g., as follows:
```
$ LD_LIBRARY_PATH=. python test.py
```
For the sake of completeness, the aforementioned [archive](http://www1.spms.ntu.edu.sg/~dima/software/dynpy.tgz) contains a C file that calls mean from libcmean.so, and the corresponding entries in the Makefile can do the necessary work to build and run it.
Last but not least,[Sage](http://www.sagemath.org/) automates parts of the interface building [even better](http://sagemath.blogspot.com/2010/11/getting-started-with-cython.html).