不依靠其余工具,直接使用Python的扩展API来编写一些简单的C扩展模块。html
本篇参考PythonCookbook第15节和Python核心编程完成,值得注意的是,Python2.X和Python3.X在扩展库写法上略有不一样,咱们研究的是3.X写法。python
Extest2.c编程
c文件头必须包含"Python.h"头,以调用接口函数bash
这里面写了两个c函数,模块名称定为Extestide
#include "Python.h" #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 (p < q) { t = *p; *p++ = *q; *q-- = t; } return s; }
对这两个函数采起C对Python API封装,函数
static PyObject* 模块名称_C函数名称(PyObject *self, PyObject *args)
PyArg_ParseTuple(args, "i", &num) //PyObject参数,类型指定,C参数存放地址1,[C参数存放地址2……]
(PyObject*)Py_BuildValue("ss", orig_str, \ dupe_str = reverse(strdup(orig_str))); //类型指定,C参数1,[C参数2……]
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 *py_func(PyObject *self, PyObject *args) { ... }
PyObject
是一个能表示任何Python对象的C数据类型。 在一个高级层面,一个扩展函数就是一个接受一个Python对象 (在 PyObject *args中)元组并返回一个新Python对象的C函数。 函数的 self
参数对于简单的扩展函数没有被使用到, 不过若是你想定义新的类或者是C中的对象类型的话就能派上用场了。好比若是扩展函数是一个类的一个方法, 那么 self
就能引用那个实例了。测试
PyArg_ParseTuple()
函数被用来将Python中的值转换成C中对应表示。 它接受一个指定输入格式的格式化字符串做为输入,好比“i”表明整数,“d”表明双精度浮点数, 一样还有存放转换后结果的C变量的地址。 若是输入的值不匹配这个格式化字符串,就会抛出一个异常并返回一个NULL值。 经过检查并返回NULL,一个合适的异常会在调用代码中被抛出。ui
Py_BuildValue()
函数被用来根据C数据类型建立Python对象。 它一样接受一个格式化字符串来指按期望类型。 在扩展函数中,它被用来返回结果给Python。 Py_BuildValue()
的一个特性是它能构建更加复杂的对象类型,好比元组和字典。 在 py_divide()
代码中,一个例子演示了怎样返回一个元组。不过,下面还有一些实例:spa
return Py_BuildValue("i", 34); // Return an integer return Py_BuildValue("d", 3.4); // Return a double return Py_BuildValue("s", "Hello"); // Null-terminated UTF-8 string return Py_BuildValue("(ii)", 3, 4); // Tuple (3, 4)
库信息以及初始化信息
/* 记录函数信息,{函数在Python中名称,函数对应封装,参数格式(此处表示参数以元组格式传入)} */ static PyMethodDef ExtestMethods[] = { {"fac", Extest_fac, METH_VARARGS}, {"doppel", Extest_doppel, METH_VARARGS}, {NULL, NULL}, }; /* Module structure */ static struct PyModuleDef Extestmodule = { PyModuleDef_HEAD_INIT, "Extest", /* 库名称 */ "A sample module", /* Doc string (may be NULL) */ -1, /* Size of per-interpreter state or -1 */ ExtestMethods /* 函数信息 */ }; /* Module initialization function */ PyMODINIT_FUNC PyInit_Extest(void) { /* PyInit_库名称 */ return PyModule_Create(&Extestmodule); /* 参数为Module structure名词 */ }
在扩展模块底部,你会发现一个函数表,好比本节中的 ExtestMethods 表。 这个表能够列出C函数、Python中使用的名字、文档字符串。 全部模块都须要指定这个表,由于它在模块初始化时要被使用到。
最后的函数 PyInit_Extest()
是模块初始化函数,但该模块第一次被导入时执行。 这个函数的主要工做是在解释器中注册模块对象。
from distutils.core import setup, Extension MOD = "Extest" setup(name=MOD, # 一个名字参数表示要编译哪一个东西
ext_modules=[ # 一个list对象列出编译对象
Extension(MOD, sources=['Extest2.c']) # 完整扩展名(可能含有.),源文件
])
调用:
python setup.py build python setup.py install
测试: