Skip to content

Latest commit

 

History

History
221 lines (184 loc) · 5.89 KB

21.md

File metadata and controls

221 lines (184 loc) · 5.89 KB

Python 扩展

写这么久的代码,你有想过扩展你的语言吗?Python 提供了扩展机制,你可以 DIY 你的 Python。 那么为什么要扩展 Python 呢?我想有一下几个理由:

  • 添加额外的功能 既然都叫扩展了,当然是为了添加额外的功能...
  • 性能瓶颈的效率提升 Python 是一种解释性语言,理论上来说它的效率会比编译型语言的效率要低,但是 Python 已经在效率上做了很多优化,如你看到的 .pyc 中间文件,他就是为了提升效率而编译的二进制码。如果 Python 还是不能满足你的效率需求,那么你可以使用 C/C++ 来扩展你的 Python。把承重的计算任务交给 C/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, 这样一个扩展就完成了。。。