为何要编写C扩展Python:
python语言的缺点
很多时候不能将程序连写成一行,如import sys;for i in sys.path:print i。而perl和awk就无此限制,可以较为方便的在shell下完成简单程序,不需要如Python一样,必须将程序写入一个.py文件。(对很多用户而言这也不算是限制)
(1)运行速度,有速度要求的话,用C++改写关键部分吧。不过对于用户而言,机器上运行速度是可以忽略的。因为用户根本感觉不出来这种速度的差异。
(2)既是优点也是缺点,python的开源性是的Python语言不能加密,但是目前国内市场纯粹靠编写软件卖给客户的越来越少,网站和移动应用不需要给客户源代码,所以这个问题就是问题了。国随着时间的推移,很多国内软件公司,尤其是游戏公司,也开始规模使用他。
(3) 构架选择太多(没有像C#这样的官方.net构架,也没有像ruby由于历史较短,构架开发的相对集中。Ruby on Rails 构架开发中小型web程序天下无敌)。不过这也从另一个侧面说明,python比较优秀,吸引的人才多,项目也多。
利用C扩展Python的好处:
- C语言编译后变成机器,相对于明文的Python,可以有效的保护核心代码
- Python运行效率慢,C执行效率高,可以有效解决性能瓶颈
- C语言可以编写很多功能,可以在Python中创建C一些特有的东西
环境
Ubuntu16.04
Python2.7~Python3.5
Python2.7编写C扩展
(Ubuntu16.04 + VS2015 + python2.7)
C代码中在实现两个功能:
-
求一个整数的阶乘
-
求一个字符串的逆序
提供test函数接口供调用,模块名设置为Extest
以下代码是Extest.c:#include <stdio.h>#include <stdlib.h>#include <string.h>int fac(int n){if (n < 2) return (1);return (n)*fac(n - 1);}char *reverse(char *s){register char t,*p = s,*q = (s + (strlen(s) - 1));while (s && (p < q)){t = *p;*p++ = *q;*q-- = t;}return(s);}int test(){char s[BUFSIZ];printf("4! == %d\n", fac(4));printf("8! == %d\n", fac(8));printf("12! == %d\n", fac(12));strcpy(s, "abcdef");printf("reversing 'abcdef', we get '%s'\n", reverse(s));strcpy(s, "madam");printf("reversing 'madam', we get '%s'\n", reverse(s));return 0;}#include "Python.h"static PyObject *Extest_fac(PyObject *self, PyObject *args){int num;if (!PyArg_ParseTuple(args, "i", &num))return NULL;return (PyObject*)Py_BuildValue("i", fac(num));}static PyObject *Extest_doppel(PyObject *self, PyObject *args){char *orig_str;char *dupe_str;PyObject* retval;if (!PyArg_ParseTuple(args, "s", &orig_str))return NULL;retval = (PyObject*)Py_BuildValue("ss", orig_str, dupe_str = reverse(strdup(orig_str)));free(dupe_str);return retval;}static PyObject *Extest_test(PyObject *self, PyObject *args){test();return (PyObject*)Py_BuildValue("");}static PyMethodDefExtestMethods[] ={{ "fac", Extest_fac, METH_VARARGS },{ "doppel", Extest_doppel, METH_VARARGS },{ "test", Extest_test, METH_VARARGS },{ NULL, NULL },};void initExtest(){Py_InitModule("Extest", ExtestMethods);}
以下代码是test-setup.py
#! /usr/bin/env pythonfrom distutils.core import setup, ExtensionMOD = 'Extest'
setup(name=MOD, ext_modules=[Extension(MOD, sources=['Extest.c'])])
运行方法:
【在python2.7环境下执行】
python test-setup.py build # 命令行执行这句会在当前目录下创建build文件夹,
cd build/lib.linux-x86_64-2.7/
进入python【python2.7】交互环境>>>
import Extest
Extest.test()
执行效果:
这样,你就能把这个生成的.so文件放入你的项目中,直接import Extest就能导入这个模块了.
Python3.5.2编写C扩展
(Ubuntu16.04 + VS2015 + python3.5.2)
C代码中在实现三个功能:
- 求一个数的绝对值
- 求一个字符串的逆序
- 打印输出测试
提供上述三个方法函数接口供调用,模块名设置为Extest
以下代码是my_extend.c:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<Python.h>int my_abs(int n) {if (n<0)n = n * -1;return n;
}void my_reverse(char *s) {if (s) {int len = strlen(s);int i;char t;for (i = 0;i<(len - 1) / 2;i++) {t = s[i];s[i] = s[len - 1 - i];s[len - 1 - i] = t;}}}void test(void) {printf("test my_abs:\n");printf("|-8590|=%d\n", my_abs(-8590));printf("|-0|=%d\n", my_abs(-0));printf("|5690|=%d\n", my_abs(-5690));printf("test my_reverse:\n");char s0[10] = "apple";char s1[20] = "I love you!";char *s2 = NULL;my_reverse(s0);my_reverse(s1);my_reverse(s2);printf("'apple' reverse is '%s'\n", s0);printf("'I love you!' reverse is '%s'\n", s1);printf("null reverse is %s\n", s2);
}//作用,接受python传的值,将结果计算后转为Python对象返回给python
//返回类型PyObject*,函数名:模块名_函数名
static PyObject *Extest_abs(PyObject *self, PyObject *args) {int num;if (!(PyArg_ParseTuple(args, "i", &num))) {return NULL;}return (PyObject*)Py_BuildValue("i", my_abs(num));
}static PyObject *Extest_reverse(PyObject *self, PyObject *args) {char *s;if (!(PyArg_ParseTuple(args, "z", &s))) {return NULL;}my_reverse(s);return (PyObject*)Py_BuildValue("s", s);
}static PyObject *Extest_test(PyObject *self, PyObject *args) {test();return (PyObject*)Py_BuildValue("");
}//为每个模块增加PyMethodDef ModuleMethods[]数组
static PyMethodDef ExtestMethods[] = {{ "abs",Extest_abs,METH_VARARGS },{ "reverse",Extest_reverse,METH_VARARGS },{ "test",Extest_test,METH_VARARGS },{ NULL,NULL },
};static struct PyModuleDef ExtestModule = {PyModuleDef_HEAD_INIT,"Extest",NULL,-1,ExtestMethods
};void PyInit_Extest() {PyModule_Create(&ExtestModule);
}
以下代码是test-setup1.py
#! /usr/bin/env python
from distutils.core import setup, Extensiondef main():MOD = 'Extest' # 模块名setup(name=MOD, ext_modules=[Extension(MOD, sources=['my_extend.c'])]) # 源文件名if __name__ == '__main__':main()
运行方法:
【在python3.5.2环境下执行】
python test-setup1.py build # 命令行执行这句会在当前目录下创建build文件夹,
cd build/lib.linux-x86_64-3.6/
进入python【python3.5.2】交互环境>>>
import Extest
Extest.test()
Extest.abs(-6)
Extest.reverse(“abcdef”)
执行效果:
附录:
python和c对应的类型转换参数表
Table 22.2 Common Codes to Convert Data Between Python and C/C++
Format Code | Python Type | C/C++ Type |
---|---|---|
s | str | char* |
z | str/None | char*/NULL |
i | int | int |
l | long | long |
c | str | char |
d | float | double |
D | complex | Py_Complex* |
O | (any) | PyObject* |
S | str | PyStringObject |
Py_BuildValue的用法表
Py_BuildValue("") | None |
---|---|
Py_BuildValue(“i”, 123) | 123 |
Py_BuildValue(“iii”, 123, 456, 789) | (123, 456, 789) |
Py_BuildValue(“s”, “hello”) | ‘hello’ |
Py_BuildValue(“ss”, “hello”, “world”) | (‘hello’, ‘world’) |
Py_BuildValue(“s#”, “hello”, 4) | ‘hell’ |
Py_BuildValue("()") | () |
Py_BuildValue("(i)", 123) | (123,) |
Py_BuildValue("(ii)", 123, 456) | (123, 456) |
Py_BuildValue("(i,i)",123, 456) | (123, 456) |
Py_BuildValue("[i, i]",123,456) | [123, 456] |
Py_BuildValue("{s:i,s:i}", “abc”, 123 “def”, 456) | {‘abc’:123, “def”:456} |
Py_BuildValue("((ii)(ii)) (ii)", 1, 2, 3, 4, 5, 6) | ((1,2),(3,4)),(5,6) |