写这么久的代码,你有想过扩展你的语言吗?Python 提供了扩展机制,你可以 DIY 你的 Python。 那么为什么要扩展 Python 呢?我想有一下几个理由:
- 添加额外的功能 既然都叫扩展了,当然是为了添加额外的功能...
- 性能瓶颈的效率提升 Python 是一种解释性语言,理论上来说它的效率会比编译型语言的效率要低,但是 Python 已经在效率上做了很多优化,如你看到的 .pyc 中间文件,他就是为了提升效率而编译的二进制码。如果 Python 还是不能满足你的效率需求,那么你可以使用 C/C++ 来扩展你的 Python。把承重的计算任务交给 C/C++。
- 代码保密 这个就不说了,还是开源精神比较好,^_^
好了,不多说,我们来做一个扩展的小例子。
MyExt.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "Python.h"
// 功能函数
static PyObject*
MyExt_mkduple(PyObject* self, PyObject* args){
char* str;
if(!PyArg_ParseTulple(args, "s", &str))
return NULL;
return (PyObject*)Py_BuildValue("ss", str, str);
}
// 模块初始化
static PyMethodDef MyExtMethods[] = {
{"mkduple", MyExt_mkduple, METH_VARARGS },
{ NULL, NULL },
};
void initMyExt(){
Py_InitModule("MyExt", MyExtMethods);
}
1.头文件包含 这个就不解释了,头文件一般在 /usr/local/include/python2.x 或 /usr/include/python2.x 中。 2.函数命名 一般功能函数的命名都为 Modul_func 如本例中我们使用 MyExt_kdduple,模块扩展好之后就如下使用:
>>>import MyExt
>>>MyExt.mkduple('abc')
...('abc','abc')
3.数据类型转换 这里就是编写 Python 扩展代码的核心了,做完数据类型的转换,其他地方就是具体功能的 C 代码实现了。 Python 到 C 的转换使用 PyArg_ParseTuple() 或者 PyArg_ParseTupleAndKeywords() 系列函数, C 到 Python 的转换使用 Py_BuildValue() 函数。 函数原型:
int PyArg_ParseTuple(PyObject *arg, char *format, ...);
int PyArg_ParseTupleAndKeywords(PyObject* arg, \
PyObject* kwdict, char* format, char* kwlist[],...);
PyObject* Py_BuildValue(char* format, ...);
PyArg_ParseTuple 的使用有点像 sprintf 函数,在例子中:
char* str;
if(!PyArg_ParseTulple(args, "s", &str))
return NULL;
Py_BuildValue d 的使用也一样,要注意的是我们返回的是一个元组,所以 format 字符串使用的 'ss':
return (PyObject*)Py_BuildValue("ss", str, str);
4.模块初始化 最后增加一个模块初始化函数,命名为 void initModule():
static PyMethodDef MyExtMethods[] = {
{"mkduple", MyExt_mkduple, METH_VARARGS },
{ NULL, NULL },
};
void initMyExt(){
Py_InitModule("MyExt", MyExtMethods);
}
下面一张转换通用代码表:
格式代码 | Python 类型 | C/C++类型 |
---|---|---|
s | str | char* |
z | str/Nome | char*/NULL |
i | int | int |
l | long | long |
c | str | char |
d | float | double |
D | complex | Py_Complex* |
O | (any) | PyObject* |
S | str | PyStringObject |
如果使用 Makefile 来编译会比较麻烦, 而且每个平台的编译器不一样,所以 Python 提供了 distutils 包用来编译、安装和分发模块、扩展和包。
setup.py
#!/usr/bin/env python
from distutils.core import setup, Extension
mod_name = 'MyExt'
mod = Extension(mod_name, sources=['MyExt.c'])
setup(name=mod_name, ext_modules=[mod])
运行 setup.py build 编译代码
$python setup.py build
running build
running build_ext
building 'MyExt' extension
creating build
creating build/temp.linux-x86_64-2.7
gcc -pthread -fno-strict-aliasing -fmessage-length=0 -grecord-gcc-switches -O2 -Wall -D_FORTIFY_SOURCE=2 -fstack-protector -funwind-tables -fasynchronous-unwind-tables -g -DNDEBUG -fmessage-length=0 -grecord-gcc-switches -O2 -Wall -D_FORTIFY_SOURCE=2 -fstack-protector -funwind-tables -fasynchronous-unwind-tables -g -DOPENSSL_LOAD_CONF -fPIC -I/usr/include/python2.7 -c MyExt.c -o build/temp.linux-x86_64-2.7/MyExt.o
In file included from /usr/include/python2.7/Python.h:8:0,
from MyExt.c:4:
/usr/include/python2.7/pyconfig.h:1185:0: warning: "_POSIX_C_SOURCE" redefined [enabled by default]
#define _POSIX_C_SOURCE 200112L
^
In file included from /usr/include/stdio.h:27:0,
from MyExt.c:1:
/usr/include/features.h:230:0: note: this is the location of the previous definition
# define _POSIX_C_SOURCE 200809L
^
creating build/lib.linux-x86_64-2.7
gcc -pthread -shared build/temp.linux-x86_64-2.7/MyExt.o -L/usr/lib64 -lpython2.7 -o build/lib.linux-x86_64-2.7/MyExt.so
运行 setup.py install 安装模块
$python setup.py install
running install
running build
running build_ext
running install_lib
copying build/lib.linux-x86_64-2.7/MyExt.so -> /usr/lib64/python2.7/site-packages
running install_egg_info
Writing /usr/lib64/python2.7/site-packages/MyExt-0.0.0-py2.7.egg-info
安装好之后我们来测试一下模块:
>>> import MyExt
>>> MyExt.mkduple('abc')
('abc', 'abc')
>>>
Ok, 这样一个扩展就完成了。。。