用C扩展Python
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用C扩展Python相关的知识,希望对你有一定的参考价值。
参考
编写Python扩展(Extending Python with C or C++)
https://docs.python.org/2.7/extending/embedding.html
环境
主机: ubuntu14.04 64bit
开发板: qemu + aarch64 (参考: http://www.cnblogs.com/pengdonglin137/p/6442583.html)
工具链: aarch64-linux-gnu-gcc (gcc version 4.9.1 20140529)
Python版本: Python-2.7.13
概述
上面参考列表中的文章已经说的很全了,这里仅作一些补充。分为三个:
1、交叉编译扩展模块到aarch64上面
2、编译扩展模块到Qemu模拟的x86_64上面
3、编译扩展模块到PC(x86_64)上面
采用的测试模块是Extend_wrap.c,这个在python扩展实现方法--python与c混和编程中有说明,源码如下:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <Python.h> 5 6 #define BUFSIZE 10 7 8 int fac(int n) { 9 if (n < 2) 10 return 1; 11 return n * fac(n - 1); 12 } 13 14 static PyObject * Extest_fac(PyObject *self, PyObject *args) { 15 int res;//计算结果值 16 int num;//参数 17 PyObject* retval;//返回值 18 19 //i表示需要传递进来的参数类型为整型,如果是,就赋值给num,如果不是,返回NULL; 20 res = PyArg_ParseTuple(args, "i", &num); 21 if (!res) { 22 //包装函数返回NULL,就会在Python调用中产生一个TypeError的异常 23 return NULL; 24 } 25 res = fac(num); 26 //需要把c中计算的结果转成python对象,i代表整数对象类型。 27 retval = (PyObject *)Py_BuildValue("i", res); 28 return retval; 29 } 30 31 char *reverse(char *s) { 32 register char t; 33 char *p = s; 34 char *q = (s + (strlen(s) - 1)); 35 while (p < q) { 36 t = *p; 37 *p++ = *q; 38 *q-- = t; 39 } 40 return s; 41 } 42 43 static PyObject * 44 Extest_reverse(PyObject *self, PyObject *args) { 45 char *orignal; 46 if (!(PyArg_ParseTuple(args, "s", &orignal))) { 47 return NULL; 48 } 49 return (PyObject *)Py_BuildValue("s", reverse(orignal)); 50 } 51 52 static PyObject * 53 Extest_doppel(PyObject *self, PyObject *args) { 54 char *orignal; 55 char *reversed; 56 PyObject * retval; 57 if (!(PyArg_ParseTuple(args, "s", &orignal))) { 58 return NULL; 59 } 60 retval = (PyObject *)Py_BuildValue("ss", orignal, reversed=reverse(strdup(orignal))); 61 free(reversed); 62 return retval; 63 } 64 65 static PyMethodDef 66 ExtestMethods[] = { 67 {"fac", Extest_fac, METH_VARARGS}, 68 {"doppel", Extest_doppel, METH_VARARGS}, 69 {"reverse", Extest_reverse, METH_VARARGS}, 70 {NULL, NULL}, 71 }; 72 73 void initExtest() { 74 Py_InitModule("Extest", ExtestMethods); 75 } 76 77 int main() { 78 char s[BUFSIZE]; 79 printf("4! == %d\\n", fac(4)); 80 printf("8! == %d\\n", fac(8)); 81 printf("12! == %d\\n", fac(12)); 82 strcpy(s, "abcdef"); 83 printf("reversing ‘abcdef‘, we get ‘%s‘\\n", reverse(s)); 84 strcpy(s, "madam"); 85 printf("reversing ‘madam‘, we get ‘%s‘\\n", reverse(s)); 86 return 0; 87 }
关于这段代码的解释,请参考python扩展实现方法--python与c混和编程
正文
1、交叉编译扩展模块到aarch64上面
这里介绍两种方法:
第一种: 将这个文件拷贝到Python2.7.3的Modules目录下面编译
拷贝:
cp Extest_wrap.c ../../Python-2.7.13/Modules/
修改Python-2.7.13/setup.py,添加模块:
1 diff --git a/setup.py b/setup.py 2 index 81355c7..5083c3d 100644 3 --- a/setup.py 4 +++ b/setup.py 5 @@ -1743,6 +1743,7 @@ class PyBuildExt(build_ext): 6 ‘-framework‘, ‘Carbon‘]) ) 7 8 9 + exts.append(Extension(‘Extest‘, [‘Extest_wrap.c‘])) 10 self.extensions.extend(exts) 11 12 # Call the method for detecting whether _tkinter can be compiled
然后执行aarch64/mk2_make.sh,可以看到build/lib.linux2-aarch64-2.7/下面已经有Extest.so了:
1 $ls build/lib.linux2-aarch64-2.7/Extest.so -l 2 -rwxrwxr-x 1 pengdonglin pengdonglin 22121 Mar 22 14:47 build/lib.linux2-aarch64-2.7/Extest.so*
然后执行aarch64/mk3_install.sh,就会将Extest.so安装到lib/python2.7/lib-dynload/下面。
最后重新制作ramdisk文件,重启板子,测试Extest.so能否使用:
1 [[email protected] root]# python 2 Python 2.7.13 (default, Mar 22 2017, 10:39:43) 3 [GCC 4.9.1 20140529 (prerelease)] on linux2 4 Type "help", "copyright", "credits" or "license" for more information. 5 >>> import Extest 6 >>> Extest.fac(4) 7 24 8 >>> Extest.reverse("abc") 9 ‘cba‘ 10 >>> Extest.doppel("abc") 11 (‘abc‘, ‘cba‘) 12 >>>
第二种: 手动编译
我们需要指定编译用的库以及头文件的搜索路径即可,下面是编译命令:
1 #!/bin/bash 2 export PATH=/home/pengdonglin/src/qemu/aarch64/gcc-linaro-aarch64-linux-gnu-4.9-2014.07_linux/bin:$PATH 3 4 CFLAGS="-I/home/pengdonglin/src/qemu/python_cross_compile/Python2/aarch64/include/python2.7 -fno-strict-aliasing -g -O2 -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes" 5 6 LDFLAGS="-L/home/pengdonglin/src/qemu/python_cross_compile/Python2/aarch64/lib -lpython2.7 -lpthread -ldl -lutil -lm -Xlinker -export-dynamic" 7 8 aarch64-linux-gnu-gcc -c ../Extest_wrap.c ${CFLAGS} -o Extest.o 9 10 aarch64-linux-gnu-gcc --shared Extest.o ${LDFLAGS} -o Extest.so
其中CFLAGS和LDFLAGS的值可以用下面的命令获得
1 $/usr/local/bin/python2-config --cflags 2 -I/usr/local/include/python2.7 -I/usr/local/include/python2.7 -fno-strict-aliasing -g -O2 -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes 3 4 $/usr/local/bin/python2-config --ldflags 5 -L/usr/local/lib/python2.7/config -lpython2.7 -lpthread -ldl -lutil -lm -Xlinker -export-dynamic
然后编译,就会在当前目录下面生成一个Extest.so,然后拷贝到板子的/usr/lib/python2.7/site-packages/下面,这个目录下存放的是一些第三方的扩展模块,而/usr/lib/python2.7/lib-dynload/存放的一般是内建模块。
2、编译扩展模块到Qemu模拟的x86_64上面
这里也有三种方法:
第一种:将Extend_wrap.c拷贝到Python源码的Modules目录下,这个前面说过,不再重复
第二种:手动编译,编译命令如下
1 #!/bin/bash 2 CFLAGS="-I/home/pengdonglin/src/qemu/python_cross_compile/Python2/x86_64/include/python2.7 -fPIC" 3 4 LDFLAGS="-L/home/pengdonglin/src/qemu/python_cross_compile/Python2/x86_64/lib -fPIC" 5 6 gcc -c ../Extest_wrap.c ${CFLAGS} -o Extest.o 7 8 gcc --shared Extest.o ${LDFLAGS} -o Extest.so
编译完成后,将Extest.so拷贝到板子上面的相应目录下即可(如/usr/lib/python2.7/site-packages)
第三种:手动编写setup.py
setup.py:
1 #!/usr/bin/env python 2 3 from distutils.core import setup, Extension 4 5 MOD = ‘Extest_x86_64‘ 6 setup(name=MOD, ext_modules=[Extension(MOD, sources=[‘Extest_wrap.c‘])])
这里模块名是Extest_x86_64,同时需要注意的是需要将setup.py跟Extest_wrap.c放到同一个目录下面。
编译:
/home/pengdonglin/src/qemu/python_cross_compile/Python2/x86_64/bin/python ./setup.py build
从log看执行的其实就是下面两条命令:
gcc -pthread -fno-strict-aliasing -g -O2 -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/pengdonglin/src/qemu/python_cross_compile/Python2/x86_64/include/python2.7 -c Extest_wrap.c -o build/temp.linux-x86_64-2.7/Extest_wrap.o creating build/lib.linux-x86_64-2.7 gcc -pthread -shared build/temp.linux-x86_64-2.7/Extest_wrap.o -o build/lib.linux-x86_64-2.7/Extest_x86_64.so
安装:
/home/pengdonglin/src/qemu/python_cross_compile/Python2/x86_64/bin/python ./setup.py install
从log看,Extest_x86_64.so会被安装到/home/pengdonglin/src/qemu/python_cross_compile/Python2/x86_64/lib/python2.7/site-packages下面
1 running install 2 running build 3 running build_ext 4 running install_lib 5 copying build/lib.linux-x86_64-2.7/Extest_x86_64.so -> /home/pengdonglin/src/qemu/python_cross_compile/Python2/x86_64/lib/python2.7/site-packages 6 running install_egg_info 7 Writing /home/pengdonglin/src/qemu/python_cross_compile/Python2/x86_64/lib/python2.7/site-packages/Extest_x86_64-0.0.0-py2.7.egg-info
然后从新制作ramdisk就可以了
3、编译扩展模块到PC(x86_64)上面
在操作之前PC上面应该用Python源码编译安装一次,方法很简单:
#!/bin/bash ../Python-2.7.13/configure make -j8 sudo make install
默认会被安装到/usr/local下面
方法一: 将Extend_wrap.c拷贝到Python源码的Modules目录下,这个前面说过,不再重复
方法二: 手动编译,编译命令如下
1 #!/bin/bash 2 CFLAGS="-I/usr/local/include/python2.7 -fPIC" 3 LDFLAGS="-L/usr/local/lib -fPIC" 4 gcc -c ../Extest_wrap.c ${CFLAGS} -o Extest.o 5 gcc --shared Extest.o ${LDFLAGS} -o Extest.so
将生成的Extest.so拷贝到/usr/local/lib/python2.7/site-packages/即可
测试:
1 $sudo cp Extest.so /usr/local/lib/python2.7/site-packages/ 2 $/usr/local/bin/python 3 Python 2.7.13 (default, Mar 22 2017, 13:18:43) 4 [GCC 4.8.4] on linux2 5 Type "help", "copyright", "credits" or "license" for more information. 6 >>> import Extest 7 >>> Extest.reverse("peng") 8 ‘gnep‘
方法三: 编写setup.py
setup.py:
1 $cat setup.py 2 #!/usr/bin/env python 3 from distutils.core import setup, Extension 4 MOD = ‘Extest‘ 5 setup(name=MOD, ext_modules=[Extension(MOD, sources=[‘Extest_wrap.c‘])])
编译:
/usr/local/bin/python ./setup.py build
从log看,执行的是下面的命令:
1 running build 2 running build_ext 3 building ‘Extest‘ extension 4 creating build 5 creating build/temp.linux-x86_64-2.7 6 gcc -pthread -fno-strict-aliasing -g -O2 -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/usr/local/include/python2.7 -c Extest_wrap.c -o build/temp.linux-x86_64-2.7/Extest_wrap.o 7 gcc -pthread -shared build/temp.linux-x86_64-2.7/Extest_wrap.o -o build/lib.linux-x86_64-2.7/Extest.so
安装:
sudo /usr/local/bin/python ./setup.py install
1 running install 2 running build 3 running build_ext 4 running install_lib 5 copying build/lib.linux-x86_64-2.7/Extest.so -> /usr/local/lib/python2.7/site-packages 6 running install_egg_info 7 Removing /usr/local/lib/python2.7/site-packages/Extest-0.0.0-py2.7.egg-info 8 Writing /usr/local/lib/python2.7/site-packages/Extest-0.0.0-py2.7.egg-info
完
以上是关于用C扩展Python的主要内容,如果未能解决你的问题,请参考以下文章
Python Cookbook(第3版)中文版:15.10 用Cython包装C代码
『Python CoolBook』C扩展库_其六_从C语言中调用Python代码