1.12.给扩展模块提供C API¶

很多扩展模块提供了新的函数和类型供Python使用,但有时扩展模块里的代码也可以被其他扩展模块使用。例如,一个扩展模块可以实现一个类型 "collection" 看起来是没有顺序的。就像是Python列表类型,拥有C API允许扩展模块来创建和维护列表,这个新的集合类型可以有一堆C函数用于给其他扩展模块直接使用。

开始看起来很简单:只需要编写函数(无需声明为 static ),提供恰当的头文件,以及C API的文档。实际上在所有扩展模块都是静态链接到Python解释器时也是可以正常工作的。当模块以共享库链接时,一个模块中的符号定义对另一个模块不可见。可见的细节依赖于操作系统,一些系统的Python解释器使用全局命名空间(例如Windows),有些则在链接时需要一个严格的已导入符号列表(一个例子是AIX),或者提供可选的不同策略(如Unix系列)。即便是符号是全局可见的,你要调用的模块也可能尚未加载。

可移植性需要不能对符号可见性做任何假设。这意味着扩展模块里的所有符号都应该声明为 static ,除了模块的初始化函数,来避免与其他扩展模块的命名冲突(在段落 模块方法表和初始化函数 中讨论) 。这意味着符号应该 必须 通过其他导出方式来供其他扩展模块访问。

Python提供了一个特别的机制来传递C级别信息(指针),从一个扩展模块到另一个:Capsules。一个Capsule是一个Python数据类型,会保存指针( void * )。Capsule只能通过其C API来创建和访问,但可以像其他Python对象一样的传递。通常,我们可以指定一个扩展模块命名空间的名字。其他扩展模块可以导入这个模块,获取这个名字的值,然后从Capsule获取指针。

Capsule可以用多种方式导出C API给扩展模块。每个函数可以用自己的Capsule,或者所有C API指针可以存储在一个数组里,数组地址再发布给Capsule。存储和获取指针也可以用多种方式,供客户端模块使用。

无论你选择哪个方法,正确地为你的 Capsule 命名都很重要。 函数 PyCapsule_New() 接受一个名称形参 (const char *);允许你传入一个 NULL 作为名称,但我们强烈建议你指定一个名称。 正确地命名的 Capsule 提供了一定程序的运行时类型安全;没有可行的方式能区分两个未命名的 Capsule。

通常来说,Capsule用于暴露C API,其名字应该遵循如下规范:

modulename.attributename

便利函数 PyCapsule_Import() 可以方便的载入通过Capsule提供的C API,仅在Capsule的名字匹配时。这个行为为C API用户提供了高度的确定性来载入正确的C API。

如下例子展示了将大部分负担交由导出模块作者的方法,适用于常用的库模块。其会存储所有C API指针(例子里只有一个)在 void 指针的数组里,并使其值变为Capsule。对应的模块头文件提供了宏来管理导入模块和获取C API指针;客户端模块只需要在访问C API前调用这个宏即可。

导出的模块修改自 spam 模块,来自 一个简单的例子 段落。函数 spam.system() 不会直接调用C库函数 system() ,但一个函数 PySpam_System() 会负责调用,当然现实中会更复杂些(例如添加 "spam" 到每个命令)。函数 PySpam_System() 也会导出给其他扩展模块。

函数 PySpam_System() 是个纯C函数,声明 static 就像其他地方那样:

static int

PySpam_System(const char *command)

{

return system(command);

}

函数 spam_system() 按照如下方式修改:

static PyObject *

spam_system(PyObject *self, PyObject *args)

{

const char *command;

int sts;

if (!PyArg_ParseTuple(args, "s", &command))

return NULL;

sts = PySpam_System(command);

return PyLong_FromLong(sts);

}

在模块开头,在此行后:

#include

添加另外两行:

#define SPAM_MODULE

#include "spammodule.h"

#define 用于告知头文件需要包含给导出的模块,而不是客户端模块。最终,模块的初始化函数必须负责初始化C API指针数组:

PyMODINIT_FUNC

PyInit_spam(void)

{

PyObject *m;

static void *PySpam_API[PySpam_API_pointers];

PyObject *c_api_object;

m = PyModule_Create(&spammodule);

if (m == NULL)

return NULL;

/* Initialize the C API pointer array */

PySpam_API[PySpam_System_NUM] = (void *)PySpam_System;

/* Create a Capsule containing the API pointer array's address */

c_api_object = PyCapsule_New((void *)PySpam_API, "spam._C_API", NULL);

if (PyModule_AddObject(m, "_C_API", c_api_object) < 0) {

Py_XDECREF(c_api_object);

Py_DECREF(m);

return NULL;

}

return m;

}

注意 PySpam_API 声明为 static ;此外指针数组会在 PyInit_spam() 结束后消失!

头文件 spammodule.h 里的一堆工作,看起来如下所示:

#ifndef Py_SPAMMODULE_H

#define Py_SPAMMODULE_H

#ifdef __cplusplus

extern "C" {

#endif

/* Header file for spammodule */

/* C API functions */

#define PySpam_System_NUM 0

#define PySpam_System_RETURN int

#define PySpam_System_PROTO (const char *command)

/* Total number of C API pointers */

#define PySpam_API_pointers 1

#ifdef SPAM_MODULE

/* This section is used when compiling spammodule.c */

static PySpam_System_RETURN PySpam_System PySpam_System_PROTO;

#else

/* This section is used in modules that use spammodule's API */

static void **PySpam_API;

#define PySpam_System \

(*(PySpam_System_RETURN (*)PySpam_System_PROTO) PySpam_API[PySpam_System_NUM])

/* Return -1 on error, 0 on success.

* PyCapsule_Import will set an exception if there's an error.

*/

static int

import_spam(void)

{

PySpam_API = (void **)PyCapsule_Import("spam._C_API", 0);

return (PySpam_API != NULL) ? 0 : -1;

}

#endif

#ifdef __cplusplus

}

#endif

#endif/* !defined(Py_SPAMMODULE_H) */

客户端模块必须在其初始化函数里按顺序调用函数 import_spam() (或其他宏)才能访问函数 PySpam_System() 。

PyMODINIT_FUNC

PyInit_client(void)

{

PyObject *m;

m = PyModule_Create(&clientmodule);

if (m == NULL)

return NULL;

if (import_spam() < 0)

return NULL;

/* additional initialization can happen here */

return m;

}

这种方法的主要缺点是,文件 spammodule.h 过于复杂。当然,对每个要导出的函数,基本结构是相似的,所以只需要学习一次。

最后需要提醒的是Capsule提供了额外的功能,用于存储在Capsule里的指针的内存分配和释放。细节参考 Python/C API参考手册的章节 胶囊 和Capsule的实现(在Python源码发行包的 Include/pycapsule.h 和 Objects/pycapsule.c )。

备注

这个函数的接口已经在标准模块 os 里了,这里作为一个简单而直接的例子。

术语"借用"一个引用是不完全正确的:拥有者仍然有引用的拷贝。

检查引用计数至少为1 没有用 ,引用计数本身可以在已经释放的内存里,并有可能被其他对象所用。

当你使用 "旧式" 风格调用约定时,这些保证不成立,尽管这依旧存在于很多旧代码中。

python扩展文件_1. 使用 C 或 C++ 扩展 Python相关推荐

  1. 手机python编程文件如何转文档_(转)Python之文件读写

    原文:https://www.cnblogs.com/huilixieqi/p/6494891.html 本节内容: I/O操作概述 文件读写实现原理与操作步骤 文件打开模式 Python文件操作步骤 ...

  2. python对文件的读操作有哪些方法-Python中文件的读取和写入操作

    从文件中读取数据 读取整个文件 这里假设在当前目录下有一个文件名为'pi_digits.txt'的文本文件,里面的数据如下: 3.1415926535 8979323846 2643383279 wi ...

  3. python读文件去除空行_「34」Python文件操作经典案例:CSV文件的读与写

    [1]认识CSV文件 CSV是Comma Separated Values的缩写,它是逗号分隔符文本格式,常用于数据交换.Excel文件和数据库数据的导入和导出. 鉴于CSV的应用场景,编程人员与它打 ...

  4. python打开文件报错无效序列_解决Python 写文件报错TypeError的问题

    处理上传的文件: f1 = request.FILES['pic'] fname = '%s/%s' % (settings.MEDIA_ROOT, f1.name) with open(fname, ...

  5. python移动文件中某个内容_如何在Python中移动文件

    如何在Python中移动文件 我查看了Python $ mv ...接口,但无法找到移动文件的方法. 我如何在Python中执行相当于$ mv ...的操作? >>> source_ ...

  6. python 清空文件夹_别这样直接运行Python命令,否则电脑等于“裸奔”

    Python已经成为全球最受欢迎的编程语言之一.原因当然是Python简明易用的脚本语法,只需把一段程序放入.py文件中,就能快速运行.而且Python语言很容易上手模块.比如你编写了一个模块my_l ...

  7. abaqus python 读取文件_ABAQUS Command 如何调用或执行 Python 脚本文件

    ABAQUS 中调用或者执行 Python 脚本有两种方法. 第一种方法,首先编写 Python 脚本文件,例如按照下面的代码编写 Python 脚本,实现批处理任务: from ABAQUS imp ...

  8. macbook自带python保存文件夹_在mac下查找python包存放路径site-packages的实现方法 在Mac系统下python如何安装第三方函数库?...

    mac怎么查看python的site-package位置世界上最伤心的事,不是你爱的人不爱你,而是他爱你过后,最后却不爱你. 可以通过find命令查看,参考demo如下: sudo find / -n ...

  9. python解题时间_1小时还是30秒?Python给你的另一种数据处理选择

    原标题:1小时还是30秒?Python给你的另一种数据处理选择 引子 想象一下,你每周都要手动重复同一过程,比如从多个来源复制数据并粘贴到一个电子表格中,用于后续处理.这项任务可能每周都需要花费一两个 ...

最新文章

  1. Enterprise Architect 中文经典教程
  2. IOS开发笔记15-自定义类
  3. 2016/09/14
  4. 多人开发时Git下冲突的产生和解决
  5. [转载]input[type=file]在移动端各浏览器无法适配打开相机的问题。
  6. 下列标识中不是c语言保留字,下列标识符中,不是 C 语言保留字的是
  7. vue 返回上一页传参_H5页面与微信小程序相互跳转并传参(web-view)
  8. 计算机学院迎新晚会集宁,迎新晚会 | 信息管理学院2017年“海姆达尔之眼”迎新晚会圆满成功...
  9. 意外分配– JIT编译抖动
  10. [牛感悟系列]JAVA(1)理解JAVA垃圾回收
  11. Thinkphp3.2在IIS中使用ISAPI_Rewrite去除index.php
  12. redis-pool go
  13. SPSS 检验后显著性识别
  14. tooltips使用教程(鼠标悬停时显示提示)
  15. 标准盒模型和怪异盒模型的区别
  16. 帝国cms系统7.5 文档word
  17. mysql 的 虚拟表(DUAL)的介绍及使用场景---条件插入insert
  18. 蓝牙时断时续很让人恼火,该如何解决(主要针对Windows 10)
  19. 【CO003】操作系统笔记3 —— IPC 问题
  20. 读书笔记--Neural Networks and Deep Learning(CH1)

热门文章

  1. 将matlab中数据输出保存为txt或dat格式
  2. MySQL排序ORDER BY与分页LIMIT,SQL,减少数据表的网络传输量,完整详细可收藏
  3. c语言队列原理的实现,c印记(十二):队列queue原理与实现
  4. 通信系统的同步技术归纳
  5. 朴素贝叶斯算法实现分类以及Matlab实现
  6. gacutil不是内部或外部命令_Win7命令提示符输入taskkill提示不是内部或外部命令...
  7. 我曾经是怎么做面试官的
  8. Zend Framework 多模块配置 (二)
  9. js中,实现对键盘按键的监听:
  10. 文件系统、mkdir、touch、nano、cp笔记