为何要编写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 PyMethodDef ExtestMethods[] = { { "fac", Extest_fac, METH_VARARGS }, { "doppel", Extest_doppel, METH_VARARGS }, { "test", Extest_test, METH_VARARGS }, { NULL, NULL }, }; void initExtest() { Py_InitModule("Extest", ExtestMethods); }
#! /usr/bin/env python
from distutils.core import setup, Extension
MOD = '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);
}
#! /usr/bin/env python
from distutils.core import setup, Extension
def 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) |