本文最后更新于 428 天前,其中的信息可能已经过时,如有错误请发送邮件到 wuxianglongblog@163.com
C Library |
Interface |
Python |
c header c implementation |
Wrapper C $\leftrightarrows$ Python communication between py + c |
import fact fact.fact(10) |
Python 扩展模块将 PyInt(10)
转化为 CInt(10)
然后调用 C
程序中的 fact()
函数进行计算,再将返回的结果转换回 PyInt
。
假设我们有这样的一个头文件和程序:
| %%file fact.h |
| |
| |
| int fact(int n); |
| |
| %%file fact.c |
| |
| int fact(int n) |
| { |
| if (n <= 1) return 1; |
| else return n * fact(n - 1); |
| } |
定义包装函数:
| %%file fact_wrap.c |
| |
| /* Must include Python.h before any standard headers*/ |
| |
| |
| static PyObject* wrap_fact(PyObject *self, PyObject *args) |
| { |
| /* Python->C data conversion */ |
| int n, result; |
| // the string i here means there is only one integer |
| if (!PyArg_ParseTuple(args, "i", &n)) |
| return NULL; |
| |
| /* C Function Call */ |
| result = fact(n); |
| |
| /* C->Python data conversion */ |
| return Py_BuildValue("i", result); |
| } |
| |
| /* Method table declaring the names of functions exposed to Python*/ |
| static PyMethodDef ExampleMethods[] = { |
| {"fact", wrap_fact, METH_VARARGS, "Calculate the factorial of n"}, |
| {NULL, NULL, 0, NULL} /* Sentinel */ |
| }; |
| |
| /* Module initialization function called at "import example"*/ |
| PyMODINIT_FUNC |
| initexample(void) |
| { |
| (void) Py_InitModule("example", ExampleMethods); |
| } |
手动使用 gcc
编译,Windows
下如果没有 gcc
,可以通过 conda
进行安装:
Window 64-bit
下编译需要加上 -DMS_WIN64
的选项,include
和 lib
文件夹的路径对应于本地 Python 安装的环境:
| !gcc -DMS_WIN64 -c fact.c fact_wrap.c -IC:\Miniconda\include |
| !gcc -DMS_WIN64 -shared fact.o fact_wrap.o -LC:\Miniconda\libs -lpython27 -o example.pyd |
Windows
下最终生成的文件后缀为 .pyd
, Unix
下生成的文件后缀名为 .so
。
用法为:
| gcc -c fact.c fact_wrap.c -I<your python path>\include |
| gcc -shared fact.o fact_wrap.o -L<your python path>\libs -lpython27 -o example.pyd |
| gcc -c fact.c fact_wrap.c -I<your python path> |
| gcc -shared fact.o fact_wrap.o -L<your python path>\config -lpython27 -o example.so |
编译完成后,我们就可以使用 example
这个模块了。
导入生成的包:
| import example |
| print dir(example) |
| ['__doc__', '__file__', '__name__', '__package__', 'fact'] |
使用 example
中的函数:
| print 'factorial of 10:', example.fact(10) |
清理刚才生成的文件:
写入 setup.py
:
| %%file setup.py |
| from distutils.core import setup, Extension |
| |
| ext = Extension(name='example', sources=['fact_wrap.c', 'fact.c']) |
| |
| setup(name='example', ext_modules=[ext]) |
使用 distutils
中的函数,我们进行 build
和 install
:
| python setup.py build ( |
| python setup.py install |
括号中的内容在 windows
中可能需要加上。
这里我们使用 build_ext --inplace
选项将其安装在本地文件夹:
| !python setup.py build_ext --inplace |
| running build_ext |
| building 'example' extension |
| creating build |
| creating build\temp.win-amd64-2.7 |
| creating build\temp.win-amd64-2.7\Release |
| C:\Miniconda\Scripts\gcc.bat -DMS_WIN64 -mdll -O -Wall -IC:\Miniconda\include -IC:\Miniconda\PC -c fact_wrap.c -o build\temp.win-amd64-2.7\Release\fact_wrap.o |
| C:\Miniconda\Scripts\gcc.bat -DMS_WIN64 -mdll -O -Wall -IC:\Miniconda\include -IC:\Miniconda\PC -c fact.c -o build\temp.win-amd64-2.7\Release\fact.o |
| writing build\temp.win-amd64-2.7\Release\example.def |
| C:\Miniconda\Scripts\gcc.bat -DMS_WIN64 -shared -s build\temp.win-amd64-2.7\Release\fact_wrap.o build\temp.win-amd64-2.7\Release\fact.o build\temp.win-amd64-2.7\Release\example.def -LC:\Miniconda\libs -LC:\Miniconda\PCbuild\amd64 -lpython27 -lmsvcr90 -o "C:\Users\Jin\Documents\Git\python-tutorial\07. interfacing with other languages\example.pyd" |
进行测试:
| import example |
| |
| print 'factorial of 10:', example.fact(10) |
定义 Python
函数:
| def pyfact(n): |
| if n <= 1: return 1 |
| return n * pyfact(n-1) |
| |
| print pyfact(10) |
| print example.fact(10) |
时间测试:
| The slowest run took 13.17 times longer than the fastest. This could mean that an intermediate result is being cached |
| 1000000 loops, best of 3: 213 ns per loop |
| 1000000 loops, best of 3: 1.43 µs per loop |
如果使用 fact
计算比较大的值:
会出现溢出的结果,因为 int
表示的值有限,但是 pyfact
不会有这样的问题:
| 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000L |
将生成的文件压缩到压缩文件中:
| import zipfile |
| |
| f = zipfile.ZipFile('07-02-example.zip','w',zipfile.ZIP_DEFLATED) |
| |
| names = 'fact.o fact_wrap.c fact_wrap.o example.pyd setup.py'.split() |
| for name in names: |
| f.write(name) |
| |
| f.close() |
清理生成的文件:
| !rm -f fact*.* |
| !rm -f example.* |
| !rm -f setup*.* |
| !rm -rf build |