Python杂项库模块与C扩展开发全解析
1. 杂项库模块概述
Python有许多虽未在详细文档中深入介绍,但仍属于标准库的模块。这些模块因各种原因(如底层性、特定平台限制、过时或过于复杂)未在之前章节提及,但可在 http://docs.python.org/library/modname 获取在线文档,所有模块索引见 http://docs.python.org/library/modindex.html 。这里列出的模块代表了Python 2和Python 3共有的功能子集,未列出的模块可能已被官方弃用,部分模块在Python 3中更名。
2. 各类杂项库模块
-
Python服务模块 :提供与Python语言和解释器执行相关的额外服务,很多涉及Python源代码的解析和编译。
| 模块 | 描述 |
| — | — |
| bdb | 访问调试器框架 |
| code | 解释器基类 |
| codeop | 编译Python代码 |
| … | … | -
字符串处理模块 :一些较旧且已过时的字符串处理模块。
| 模块 | 描述 |
| — | — |
| difflib | 计算字符串差异 |
| fpformat | 浮点数格式化 |
| stringprep | 互联网字符串准备 |
| … | … | -
操作系统模块 :提供额外的操作系统服务,部分功能可能已包含在其他相关模块中。
| 模块 | 描述 |
| — | — |
| crypt | 访问UNIX加密函数 |
| curses | Curses库接口 |
| grp | 访问组数据库 |
| … | … | -
网络模块 :支持一些较少使用的网络协议。
| 模块 | 描述 |
| — | — |
| imaplib | IMAP协议 |
| nntplib | NNTP协议 |
| poplib | POP3协议 |
| … | … | -
互联网数据处理模块 :提供未在其他章节涵盖的互联网数据处理支持。
| 模块 | 描述 |
| — | — |
| binhex | 支持BinHex4文件格式 |
| formatter | 通用输出格式化 |
| mailcap | 处理Mailcap文件 |
| … | … | -
国际化模块 :用于编写国际化应用程序。
| 模块 | 描述 |
| — | — |
| gettext | 多语言文本处理服务 |
| locale | 系统提供的国际化功能 | -
多媒体服务模块 :支持处理各种多媒体文件。
| 模块 | 描述 |
| — | — |
| audioop | 操作原始音频数据 |
| aifc | 读写AIFF和AIFC文件 |
| sunau | 读写Sun AU文件 |
| … | … | -
杂项模块 :难以归类到其他类别的模块。
| 模块 | 描述 |
| — | — |
| cmd | 面向行的命令解释器 |
| calendar | 日历生成函数 |
| shlex | 简单词法分析模块 |
| … | … |
3. Python与C集成概述
Python强大的特性之一是能与C语言编写的软件交互,常见的集成策略有两种:
- 扩展模块 :将外部函数封装成Python库模块,通过 import 语句使用,为Python应用提供高性能编程库功能。
- 嵌入 :从C语言将Python程序和解释器作为库访问,常用于将Python解释器嵌入现有C应用框架作为脚本引擎。
4. 扩展模块开发
4.1 示例代码基础
以一个简单的C库为例,包含头文件 example.h 、实现文件 example.c 和测试主程序 main.c 。
// example.h
#include <stdio.h>
#include <string.h>
#include <math.h>
typedef struct Point {
double x;
double y;
} Point;
extern int gcd(int x, int y);
extern int replace(char *s, char och, char nch);
extern double distance(Point *a, Point *b);
#define MAGIC 0x31337
// example.c
#include "example.h"
int gcd(int x, int y) {
int g;
g = y;
while (x > 0) {
g = x;
x = y % x;
y = g;
}
return g;
}
int replace(char *s, char oldch, char newch) {
int nrep = 0;
while (s = strchr(s,oldch)) {
*(s++) = newch;
nrep++;
}
return nrep;
}
double distance(Point *a, Point *b) {
double dx,dy;
dx = a->x - b->x;
dy = a->y - b->y;
return sqrt(dx*dx + dy*dy);
}
// main.c
#include "example.h"
int main() {
// Test the gcd() function
{
printf("%d\n", gcd(128,72));
printf("%d\n", gcd(37,42));
}
// Test the replace() function
{
char s[] = "Skipping along unaware of the unspeakable peril.";
int nrep;
nrep = replace(s,' ','-');
printf("%d\n", nrep);
printf("%s\n",s);
}
// Test the distance() function
{
Point a = { 10.0, 15.0 };
Point b = { 13.0, 11.0 };
printf("%0.2f\n", distance(&a,&b));
}
}
运行 main.c 程序的输出如下:
8
1
6
Skipping-along-unaware-of-the-unspeakable-peril.
5.00
4.2 扩展模块原型
扩展模块通过编写包含包装函数的C源文件构建,以下是一个名为 _example 的基本扩展模块示例:
// pyexample.c
#include "Python.h"
#include "example.h"
static char py_gcd_doc[] = "Computes the GCD of two integers";
static PyObject *
py_gcd(PyObject *self, PyObject *args) {
int x,y,r;
if (!PyArg_ParseTuple(args,"ii:gcd",&x,&y)) {
return NULL;
}
r = gcd(x,y);
return Py_BuildValue("i",r);
}
static char py_replace_doc[] = "Replaces all characters in a string";
static PyObject *
py_replace(PyObject *self, PyObject *args, PyObject *kwargs) {
static char *argnames[] = {"s","och","nch",NULL};
char *s,*sdup;
char och, nch;
int nrep;
PyObject *result;
if (!PyArg_ParseTupleAndKeywords(args,kwargs, "scc:replace",
argnames, &s, &och, &nch)) {
return NULL;
}
sdup = (char *) malloc(strlen(s)+1);
strcpy(sdup,s);
nrep = replace(sdup,och,nch);
result = Py_BuildValue("(is)",nrep,sdup);
free(sdup);
return result;
}
static char py_distance_doc[] = "Computes the distance between two points";
static PyObject *
py_distance(PyObject *self, PyObject *args) {
PyErr_SetString(PyExc_NotImplementedError,"distance() not implemented.");
return NULL;
}
static PyMethodDef _examplemethods[] = {
{"gcd", py_gcd, METH_VARARGS, py_gcd_doc},
{"replace", py_replace, METH_VARARGS | METH_KEYWORDS, py_replace_doc},
{"distance",py_distance,METH_VARARGS, py_distance_doc},
{NULL, NULL, 0, NULL}
};
#if PY_MAJOR_VERSION < 3
// Python 2 module initialization
void init_example(void) {
PyObject *mod;
mod = Py_InitModule("_example", _examplemethods);
PyModule_AddIntMacro(mod,MAGIC);
}
#else
// Python 3 module initialization
static struct PyModuleDef _examplemodule = {
PyModuleDef_HEAD_INIT,
"_example", /* name of module */
NULL, /* module documentation, may be NULL */
-1,
_examplemethods
};
PyMODINIT_FUNC
PyInit__example(void) {
PyObject *mod;
mod = PyModule_Create(&_examplemodule);
PyModule_AddIntMacro(mod, MAGIC);
return mod;
}
#endif
扩展模块开发要点如下:
- 必须包含 Python.h 头文件。
- 为每个要访问的C函数编写包装函数,接受两个或三个 PyObject * 类型的参数。
- 使用 PyArg_ParseTuple() 或 PyArg_ParseTupleAndKeywords() 将Python参数转换为C参数,使用 Py_BuildValue() 构建返回的Python对象。
- 包装函数的文档字符串应单独存储,在模块初始化时引用。
- 包装函数不应修改从解释器接收的引用数据,避免违反Python字符串不可变性。
- 使用 PyErr_SetString() 等函数抛出异常,返回 NULL 表示出错。
- 方法表 _examplemethods 用于关联Python名称和C包装函数,设置调用约定和文档字符串。
- 模块初始化过程在Python 2和Python 3中有所不同,命名至关重要,否则解释器无法正确加载模块。
4.3 扩展模块命名
C扩展模块通常以 _ 开头命名,如 _example ,Python标准库也遵循此惯例。一般不直接使用C扩展模块,而是创建高级Python模块进行包装,例如:
# example.py
from _example import *
# Add additional support code below
...
这样做的目的是为模块提供额外支持代码或更高级的接口,许多标准库模块也是以C和Python混合的方式实现。
4.4 编译和打包扩展模块
推荐使用 distutils 编译和打包扩展模块,创建 setup.py 文件:
# setup.py
from distutils.core import setup, Extension
setup(name="example",
version="1.0",
py_modules = ['example.py'],
ext_modules = [
Extension("_example",
["pyexample.c","example.c"])
]
)
构建测试模块的步骤如下:
1. 执行 python setup.py build_ext --inplace ,将扩展代码编译为共享库并放在当前工作目录,库名可能为 _examplemodule.so 、 _examplemodule.pyd 等。
2. 若编译成功,可直接使用模块,示例如下:
% python3.0
Python 3.0 (r30:67503, Dec 4 2008, 09:40:15)
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import example
>>> example.gcd(78,120)
6
>>> example.replace("Hello World",' ','-')
(1, 'Hello-World')
>>> example.distance()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NotImplementedError: distance() not implemented.
>>>
对于更复杂的扩展模块,可在 setup.py 中提供额外的构建信息,如包含目录、库和预处理器宏:
# setup.py
from distutils.core import setup, Extension
setup(name="example",
version="1.0",
py_modules = ['example.py'],
ext_modules = [
Extension("_example",
["pyexample.c","example.c"],
include_dirs = ["/usr/include/X11","/opt/include"],
define_macros = [('DEBUG',1),
('MONDO_FLAG',1)],
undef_macros = ['HAVE_FOO','HAVE_NOT'],
library_dirs= ["/usr/lib/X11", "/opt/lib"],
libraries = [ "X11", "Xt", "blah" ])
]
)
若要安装扩展模块供一般使用,执行 python setup.py install 。在某些情况下,也可手动构建扩展模块,但通常需要高级的编译器和链接器知识,例如在Linux上的示例:
linux % gcc -c -fpic -I/usr/local/include/python2.6 example.c pyexample.c
linux % gcc -shared example.o pyexample.o -o _examplemodule.so
5. 类型转换
5.1 从Python到C的类型转换
扩展模块使用以下函数将Python参数转换为C参数:
- int PyArg_ParseTuple(PyObject *args, char *format, ...) :解析位置参数元组, format 包含转换格式代码,其余参数为C变量地址。
- int PyArg_ParseTupleAndKeywords(PyObject *args, PyObject *kwargs, char *format, char **kwlist, ...) :解析位置参数元组和关键字参数字典, kwlist 为参数名列表。
不同类型的转换格式代码及对应的C参数类型如下:
- 数字转换
| 格式 | Python类型 | C参数类型 |
| — | — | — |
| “b” | 整数 | signed char r |
| “B” | 整数 | unsigned char r |
| … | … | … |
-
字符串和字节转换
| 格式 | Python类型 | C参数类型 |
| — | — | — |
| “c” | 长度为1的字符串或字节串 | char r |
| “s” | 字符串 | char *r |
| … | … | … | -
Python对象转换
| 格式 | Python类型 | C类型 |
| — | — | — |
| “O” | 任意 | PyObject r |
| “O!” | 任意 | PyTypeObject *type, PyObject r |
| … | … | … |
字符串处理对于C扩展是个特殊问题,不同转换代码有不同的处理方式,如处理文本时使用特定代码,避免输入包含嵌入式 NULL 字符,部分代码需要手动释放内存等。
参数格式字符串还包含一些额外修饰符:
| 格式字符串 | 描述 |
| — | — |
| “(items)” | 解包对象元组, items 为格式转换代码 |
| “|” | 可选参数开始 |
| “:” | 参数结束,后续文本为函数名 |
| “;” | 参数结束,后续文本为错误消息 |
5.2 从C到Python的类型转换
使用 PyObject *Py_BuildValue(char *format, ...) 将C变量转换为Python对象, format 描述转换格式,其余参数为要转换的C变量值。格式说明符与 PyArg_ParseTuple* 函数类似,示例如下:
| 格式 | Python类型 | C类型 | 描述 |
| — | — | — | — |
| “” | None | void | 无 |
| “s” | 字符串 | char * | 以 NULL 结尾的字符串, NULL 指针返回 None |
| … | … | … | … |
6. 模块值添加
在扩展模块的初始化函数中,可使用以下函数添加常量和其他支持值:
- int PyModule_AddObject(PyObject *module, const char *name, PyObject *value) :向模块添加新值。
- int PyModule_AddIntConstant(PyObject *module, const char *name, long value) :添加整数值。
- void PyModule_AddStringConstant(PyObject *module, const char *name, const char *value) :添加字符串值, value 必须以 NULL 结尾。
- void PyModule_AddIntMacro(PyObject *module, macro) :将预处理器宏值作为整数添加到模块。
- void PyModule_AddStringMacro(PyObject *module, macro) :将预处理器宏值作为字符串添加到模块。
7. 错误处理
扩展模块通过返回 NULL 向解释器指示错误,使用以下函数设置异常:
- void PyErr_NoMemory() :抛出 MemoryError 异常。
- void PyErr_SetFromErrno(PyObject *exc) :根据 errno 变量抛出异常。
- void PyErr_SetFromErrnoWithFilename(PyObject *exc, char *filename) :类似上一个函数,但包含文件名。
- void PyErr_SetObject(PyObject *exc, PyObject *val) :抛出指定值的异常。
- void PyErr_SetString(PyObject *exc, char *msg) :抛出带消息的异常。
exc 参数可设置为多种异常类型,如 PyExc_ArithmeticError 对应 ArithmeticError 等。还可使用以下函数查询或清除异常状态:
- void PyErr_Clear() :清除之前抛出的异常。
- PyObject *PyErr_Occurred() :检查是否抛出异常,返回当前异常值或 NULL 。
- int PyErr_ExceptionMatches(PyObject *exc) :检查当前异常是否匹配指定异常。
在C中实现 try-except 块的示例如下:
/* Carry out some operation involving Python objects */
if (PyErr_Occurred()) {
if (PyErr_ExceptionMatches(PyExc_ValueError)) {
/* Take some kind of recovery action */
...
PyErr_Clear();
return result; /* A valid PyObject * */
} else {
return NULL; /* Propagate the exception to the interpreter */
}
}
8. 引用计数
C扩展可能需要操作Python对象的引用计数,使用以下宏:
| 宏 | 描述 |
| — | — |
| Py_INCREF(obj) | 增加非空对象的引用计数 |
| Py_DECREF(obj) | 减少非空对象的引用计数 |
| Py_XINCREF(obj) | 增加可能为空对象的引用计数 |
| Py_XDECREF(obj) | 减少可能为空对象的引用计数 |
一般情况下,C扩展函数无需担心引用计数,但在以下情况需要注意:
- 保存Python对象引用供后续使用或存储在C结构中时,必须增加引用计数。
- 释放之前保存的对象时,减少引用计数。
- 从C操作Python容器(如列表、字典)时,可能需要手动操作单个项的引用计数。
若扩展代码导致解释器崩溃或内存泄漏,可能存在引用计数问题。
9. 线程处理
Python使用全局解释器锁防止多个线程同时在解释器中执行。如果扩展模块中的函数执行时间较长,会阻塞其他线程。若扩展模块是线程安全的,可使用以下宏释放和重新获取全局解释器锁:
- Py_BEGIN_ALLOW_THREADS :释放全局解释器锁,允许其他线程运行,释放期间C扩展不得调用Python C API函数。
- Py_END_ALLOW_THREADS :重新获取全局解释器锁,扩展会阻塞直到成功获取锁。
综上所述,Python杂项库模块为开发者提供了丰富的功能,而C扩展开发则让Python能够与高性能的C代码进行交互,进一步提升了Python的应用范围和性能。开发者在实际应用中可根据需求合理选择和使用这些模块和开发方法。
Python杂项库模块与C扩展开发全解析
10. ctypes库模块
ctypes 是一个极其有用的模块,它允许你在不编写额外C代码或使用C编译器的情况下访问C库中的函数。虽然文档没有详细给出 ctypes 的使用示例,但它为Python与C库交互提供了一种便捷途径。在处理一些简单的C库调用时, ctypes 可以大大简化开发流程。
11. 高级扩展和嵌入工具
对于高级的扩展和嵌入应用,大多数程序员会转向高级代码生成器和编程库。例如,SWIG项目( http://www.swig.org )是一个编译器,它通过解析C头文件的内容来创建Python扩展模块。更多关于扩展构建工具的参考可以在 http://wiki.python.org/moin/IntegratingPythonWithOtherLanguages 找到。
12. 开发流程总结
下面通过一个mermaid流程图来总结扩展模块的开发流程:
graph LR
A[定义C库功能] --> B[编写扩展模块包装函数]
B --> C[进行模块初始化]
C --> D{Python版本}
D -- Python 2 --> E[使用init_xxx初始化]
D -- Python 3 --> F[使用PyInit_xxx初始化]
E --> G[编译和打包]
F --> G
G --> H[创建高级Python模块包装]
H --> I[测试和使用模块]
13. 常见问题及解决方法
在扩展模块开发过程中,可能会遇到一些常见问题,以下是一些问题及解决方法:
| 问题 | 可能原因 | 解决方法 |
| — | — | — |
| 解释器无法加载模块 | 模块初始化函数命名错误 | 确保Python 2使用 initmodname() ,Python 3使用 PyInit_modname() |
| 包装函数返回错误结果 | 参数转换错误 | 检查 PyArg_ParseTuple() 或 PyArg_ParseTupleAndKeywords() 的格式代码和参数类型 |
| 内存泄漏 | 引用计数管理不当 | 确保在保存对象引用时增加引用计数,释放对象时减少引用计数 |
| 字符串处理异常 | 输入包含嵌入式 NULL 字符或未正确释放内存 | 使用合适的转换代码,避免输入包含嵌入式 NULL ,手动释放需要释放的内存 |
14. 性能优化建议
为了提高扩展模块的性能,可以考虑以下建议:
- 减少内存分配 :在包装函数中尽量减少不必要的内存分配,例如避免多次复制字符串。
- 优化算法 :在C代码中使用高效的算法,例如在 gcd 函数中使用欧几里得算法。
- 合理使用线程 :对于长时间运行的函数,使用 Py_BEGIN_ALLOW_THREADS 和 Py_END_ALLOW_THREADS 释放和重新获取全局解释器锁,避免阻塞其他线程。
15. 实际应用案例
假设我们要开发一个图像处理的扩展模块,使用C语言实现一些高性能的图像处理算法,如高斯模糊、边缘检测等。以下是一个简化的开发步骤:
1. 定义C库功能 :编写C代码实现图像处理算法,例如高斯模糊函数。
// image_processing.h
#include <stdio.h>
#include <stdlib.h>
// 高斯模糊函数
void gaussian_blur(unsigned char *image, int width, int height, int radius);
// image_processing.c
#include "image_processing.h"
#include <math.h>
void gaussian_blur(unsigned char *image, int width, int height, int radius) {
// 实现高斯模糊算法
// ...
}
- 编写扩展模块包装函数 :创建
pyimage_processing.c文件,编写包装函数。
// pyimage_processing.c
#include "Python.h"
#include "image_processing.h"
static char py_gaussian_blur_doc[] = "Apply Gaussian blur to an image";
static PyObject *
py_gaussian_blur(PyObject *self, PyObject *args) {
unsigned char *image;
int width, height, radius;
if (!PyArg_ParseTuple(args, "s#ii", &image, &width, &height, &radius)) {
return NULL;
}
gaussian_blur(image, width, height, radius);
return Py_BuildValue("s#", image, width * height);
}
static PyMethodDef _image_processingmethods[] = {
{"gaussian_blur", py_gaussian_blur, METH_VARARGS, py_gaussian_blur_doc},
{NULL, NULL, 0, NULL}
};
#if PY_MAJOR_VERSION < 3
// Python 2 module initialization
void init_image_processing(void) {
PyObject *mod;
mod = Py_InitModule("_image_processing", _image_processingmethods);
}
#else
// Python 3 module initialization
static struct PyModuleDef _image_processingmodule = {
PyModuleDef_HEAD_INIT,
"_image_processing", /* name of module */
NULL, /* module documentation, may be NULL */
-1,
_image_processingmethods
};
PyMODINIT_FUNC
PyInit__image_processing(void) {
PyObject *mod;
mod = PyModule_Create(&_image_processingmodule);
return mod;
}
#endif
- 编译和打包 :创建
setup.py文件,使用distutils进行编译和打包。
# setup.py
from distutils.core import setup, Extension
setup(name="image_processing",
version="1.0",
py_modules = ['image_processing.py'],
ext_modules = [
Extension("_image_processing",
["pyimage_processing.c", "image_processing.c"])
]
)
- 创建高级Python模块包装 :创建
image_processing.py文件,提供更高级的接口。
# image_processing.py
from _image_processing import *
def apply_gaussian_blur(image, width, height, radius):
# 可以添加额外的参数检查和处理
return gaussian_blur(image, width, height, radius)
- 测试和使用模块 :在Python中测试模块。
import image_processing
# 假设image是一个图像数据的字节串
image = b'\x00' * 100 * 100
width = 100
height = 100
radius = 3
blurred_image = image_processing.apply_gaussian_blur(image, width, height, radius)
print(len(blurred_image))
16. 总结与展望
Python杂项库模块涵盖了各种不同领域的功能,为开发者提供了丰富的工具。而与C语言的集成,特别是扩展模块的开发,让Python能够充分利用C语言的高性能,进一步拓展了Python的应用场景。在未来的开发中,随着Python和C语言的不断发展,扩展模块的开发将更加便捷和高效,同时也会有更多的高级工具和技术涌现,帮助开发者更好地实现Python与C的集成。开发者可以根据具体需求,灵活运用这些知识和工具,开发出更加优秀的Python应用程序。
超级会员免费看
129

被折叠的 条评论
为什么被折叠?



