使用Python的 C 扩展模块
直接使用 Python 的扩展 API 编写一个简单的 C 扩展模块,无需其他工具。为简单的 C 代码制作手工扩展模块很简单。但首先,我们必须确保 C 代码具有正确的头文件。
代码#1:
#include
extern int gcd(int, int);
extern int in_mandel(double x0, double y0, int n);
extern int divide(int a, int b, int *remainder);
extern double avg(double *a, int n);
typedef struct Point
{
double x, y;
} Point;
extern double distance(Point *p1, Point *p2);
标头将对应于已单独编译的库。下面的代码说明了按照这个假设编写扩展函数的基础知识。
代码#2:
# include "Python.h"
# include "sample.h"
/* int gcd(int, int) */
static PyObject * py_gcd(PyObject * self, PyObject * args)
{
int x, y, result;
if (! PyArg_ParseTuple(args, "ii", &x, &y))
{
return NULL;
}
result = gcd(x, y);
return Py_BuildValue("i", result);
}
/* int divide(int, int, int *) */
static PyObject * py_divide(PyObject * self, PyObject * args)
{
int a, b, quotient, remainder;
if (! PyArg_ParseTuple(args, "ii", &a, &b))
{
return NULL;
}
quotient = divide(a, b, &remainder);
return Py_BuildValue("(ii)", quotient, remainder);
}
代码#3:模块方法表和结构
/* Module method table */
static PyMethodDef SampleMethods[] =
{
{"gcd", py_gcd, METH_VARARGS, "Greatest common divisor"},
{"divide", py_divide, METH_VARARGS, "Integer division"},
{ NULL, NULL, 0, NULL}
};
/* Module structure */
static struct PyModuleDef samplemodule =
{
PyModuleDef_HEAD_INIT,
"sample", /* name of module */
"A sample module", /* Doc string (may be NULL) */
-1, /* Size of per-interpreter state or -1 */
SampleMethods /* Method table */
};
/* Module initialization function */
PyMODINIT_FUNC
PyInit_sample(void)
{
return PyModule_Create(&samplemodule);
}
代码 #4:创建setup.py
Python文件来构建扩展模块。
# setup.py
from distutils.core import setup, Extension
setup(name='sample',
ext_modules=[
Extension('sample',
['pysample.c'],
include_dirs = ['/some/dir'],
define_macros = [('FOO','1')],
undef_macros = ['BAR'],
library_dirs = ['/usr/local/lib'],
libraries = ['sample']
)
]
)
代码 #5:现在只需使用 python3 buildlib.py build_ext --inplace
来构建生成的库。
bash% python3 setup.py build_ext --inplace
running build_ext
building 'sample' extension
gcc -fno-strict-aliasing -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes
-I/usr/local/include/python3.3m -c pysample.c
-o build/temp.macosx-10.6-x86_64-3.3/pysample.o
gcc -bundle -undefined dynamic_lookup
build/temp.macosx-10.6-x86_64-3.3/pysample.o \
-L/usr/local/lib -lsample -o sample.so
bash %
上面的代码将创建一个名为sample.so的共享库。代码#6:
import sample
print ("gcd = ", sample.gcd(35, 42))
print ("\ndistance : ", sample.divide(42, 8))
输出 :
gcd = 7
distance = (5, 2)
“Extending and Embedding the Python Interpreter”是 Python 的文档,可以在尝试任何类型的手写扩展之前查阅。
在扩展模块中,可以编写如下代码片段所示的函数。
代码#4:
static PyObject *py_func(PyObject *self, PyObject *args)
{
...
}
- PyObject – 代表任何Python对象的 C 数据类型。在非常高的层次上,扩展函数是一个 C函数,它接收Python对象的元组(在
PyObject *args
中)并返回一个新的Python对象作为结果。函数的 self 参数不用于简单的扩展函数,但如果你想在 C 中定义新的类或对象类型,它就会发挥作用。 -
PyArg_ParseTuple()
函数用于将值从Python转换为 C 表示。作为输入,它采用一个格式字符串来指示所需的值,例如“i”表示整数,“d”表示双精度,以及用于放置转换结果的 C 变量的地址。 -
Py_BuildValue()
函数用于从 C 数据类型创建Python对象。它还接受格式代码来指示所需的类型。在扩展函数中,它用于将结果返回给Python。Py_BuildValue()
的一个特点是它可以构建更复杂的对象类型,例如元组和字典。