不依靠其他工具,直接使用Python的扩展API来编写一些简单的C扩展模块。

本篇参考PythonCookbook第15节和Python核心编程完成,值得注意的是,Python2.X和Python3.X在扩展库写法上略有不同,我们研究的是3.X写法。

一、源文件

Extest2.c

C函数本体

c文件头必须包含"Python.h"头,以调用接口函数

这里面写了两个c函数,模块名称定为Extest

#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;
}

Python API封装

对这两个函数采取C对Python API封装,

  • 封装函数为静态函数,输入返回均为PyObject*,且输入须有一个self用于处理类对自身的引用,函数名须为模块名_c函数名格式(模块名不是强制的)

    • static PyObject*
      模块名称_C函数名称(PyObject *self, PyObject *args)
  • Python对象->C对象,用于C函数输入,使用PyArg_ParseTuple为元组输入解析类型,另有PyArg_ParseTupleAndKeywords为字典类型解析类型
    • PyArg_ParseTuple(args, "i", &num)  //PyObject参数,类型指定,C参数存放地址1,[C参数存放地址2……]
  • C对象->Python对象,用于C函数输出,使用(PyObject*)Py_BuildValue进行类型转换
    • (PyObject*)Py_BuildValue("ss", orig_str, \
      dupe_str = reverse(strdup(orig_str))); //类型指定,C参数1,[C参数2……]
  • return Python对象,由于是个元组,所以上一步多少个C输出都没有关系
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,一个合适的异常会在调用代码中被抛出。

Py_BuildValue() 函数被用来根据C数据类型创建Python对象。 它同样接受一个格式化字符串来指定期望类型。 在扩展函数中,它被用来返回结果给Python。 Py_BuildValue() 的一个特性是它能构建更加复杂的对象类型,比如元组和字典。 在 py_divide() 代码中,一个例子演示了怎样返回一个元组。不过,下面还有一些实例:

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() 是模块初始化函数,但该模块第一次被导入时执行。 这个函数的主要工作是在解释器中注册模块对象。

二、编译文件setup.py

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

测试:

『Python CoolBook』C扩展库_其一_用法讲解的更多相关文章

  1. 『Python CoolBook』C扩展库_其三_简单数组操作

    点击进入项目 这里的数组要点在于: 数组结构,array.array或者numpy.array 本篇的数组仅限一维,不过基础的C数组也是一维 一.分块讲解 源函数 /* Average values ...

  2. 『Python CoolBook』C扩展库_其五_C语言层面Python库之间调用API

    点击进入项目 一.C层面模块添加API 我们仍然操作如下结构体, #include <math.h> typedef struct Point { double x,y; } Point; ...

  3. 『Python CoolBook』C扩展库_其六_线程

    GIL操作 想让C扩展代码和Python解释器中的其他进程一起正确的执行, 那么你就需要去释放并重新获取全局解释器锁(GIL). 在Python接口封装中去释放并重新获取全局解释器锁(GIL),此时本 ...

  4. 『Python CoolBook』C扩展库_其二_demo演示

    点击进入项目 C函数源文件 /* sample.c */ #include "sample.h" /* Compute the greatest common divisor */ ...

  5. 『Python CoolBook』C扩展库_其四_结构体操作与Capsule

    点击进入项目 一.Python生成C语言结构体 C语言中的结构体传给Python时会被封装为胶囊(Capsule), 我们想要一个如下结构体进行运算,则需要Python传入x.y两个浮点数, type ...

  6. 『Python CoolBook』C扩展库_其六_从C语言中调用Python代码

    点击进入项目 一.C语言运行pyfun的PyObject对象 思路是在C语言中提供实参,传给python函数: 获取py函数对象(PyObject),函数参数(C类型) 获取GIL(PyGILStat ...

  7. 『Python CoolBook』使用ctypes访问C代码_下_demo进阶

    点击进入项目 这一次我们尝试一下略微复杂的c程序. 一.C程序 头文件: #ifndef __SAMPLE_H__ #define __SAMPLE_H__ #include <math.h&g ...

  8. 『Python CoolBook』使用ctypes访问C代码_上_用法讲解

    一.动态库文件生成 源文件hello.c #include "hello.h" #include <stdio.h> void hello(const char *na ...

  9. 『Python CoolBook』Cython

    github地址 使用Cython导入库的话,需要一下几个文件: .c:C函数源码 .h:C函数头 .pxd:Cython函数头 .pyx:包装函数 setup.py:python 本节示例.c和.h ...

随机推荐

  1. sql 范式:1NF、2NF、3NF、BCNF(函数依赖)

    第一范式(1NF) 每个属性都是不可分的基本数据项.(必须有主键,列不可分) eg:非第一范式的表:(列可再分) 学院名称 高级职称人数 教授 副教授 信电学院 3 34 管理学院 5 23 外语学院 ...

  2. MyBatisPlus忽略映射字段注解

    MyBatisPlus忽略映射字段注解 @TableField(exist = false):表示该属性不为数据库表字段,但又是必须使用的. @TableField(exist = true):表示该 ...

  3. python迭代-可迭代对象与迭代器对象

    可迭代对象与迭代器对象 问题举例 某软件要求,从网络抓取各个城市的气温信息,并依次显示: 北京:15~22 上海:18~23 ...... 如果一次抓取所有城市气温信息再显示,显示第一个城市的气温时会 ...

  4. Docker:Dockerfile命令详解

    1.FROM 功能为指定基础镜像,并且必须是第一条指令. 如果不以任何镜像为基础,那么写法为:FROM scratch. 同时意味着接下来所写的指令将作为镜像的第一层开始 语法: FROM <i ...

  5. 【转】Robot Framework用法总结

    今天总结下Robot Framework最基本的用法,一来呢,希望自己以后看到这篇总结,很快能回忆起如何使用Robot Framework.二来呢,以初学者的姿态总结Robot Framework,希 ...

  6. MSG结构体和WndProc窗口过程详解

    MSG结构体和WndProc窗口过程对于Windows编程非常重要,如果不了解它们,可以说就没有学会Windows编程. MSG结构体 MSG 结构体用来表示一条消息,各个字段的含义如下: typed ...

  7. HTML5入门教程:响应式页面布局

    摘自:https://www.sohu.com/a/225633935_647584 随着互联网时代的发展,我们对网页布局有了新的要求,大气,美观,能够在不同的设备上呈现令人焕然一新的效果.此时,一个 ...

  8. WSDL文档结构图

  9. Docker Compose 创建yml 简单试例

    Docker Compose 创建yml 简单试例 Docker Compose 文件使用格式版本需要与Docker版本对应可在官网内查找 查找地址:https://docs.docker.com/c ...

  10. 剑指offer(63)数据流中的中位数

    题目描述 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值.如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值.我们 ...