这是一个非常流行的设计模式,将函数实现的部分工作委托给用户提供的自定义回调。
在C 标准库中,最著名的接受这种回调的函数是qsort()函数,它提供了Quicksort 算法
的通用实现。你不太可能会直接使用这种算法,而是使用更适合Python 集合排序的默认
Python Timsort 排序方法。无论如何,qsort()似乎是一个有效的排序算法的典型示例,并
且使用许多编程书把它作为在C API 中使用回调机制I 的规范示例。这就是为什么我们将
尝试使用它作为传递Python 函数作为C 回调的例子。
普通的Python 函数类型与qsort()规范所需的回调函数类型不兼容。这里是来自BSD
手册页的qsort()的签名,它还包含接受的回调类型(compare 参数)的类型,如下所示:
void qsort(void *base, size_t nel, size_t width,
int (*compar)(const void , const void ));
所以为了从libc 执行qsort(),你需要传递。
● base:这是需要排序的数组,它是void
指针类型。
● nel:这是元素个数,类型为size_t。
● width:这是数组中单个元素的大小,类型为size_t。
● comparator:这是指向返回值为int 类型并接受两个void
指针类型的函数的指
针。它指向比较需要排序的两个元素的大小的函数。
在使用ctypes 调用C 函数这部分,我们已经知道如何使用乘法运算符从其他ctypes
类型构造C 数组。nel 的类型是size_t,它映射到Python 的int 类型,所以它不需要
任何额外的包装,可以作为len(迭代器)传递。一旦我们知道base 数组的类型,width
的值就可以使用ctypes.sizeof()函数获得。我们需要知道的最后一件事是如何创建一
个指针,该指针指向与comparator 参数兼容的Python 函数。
ctypes 模块包含一个CFUNCTYPE()工厂函数,它允许我们包装Python 函数,并将
它们表示为C 可调用的函数指针。第一个参数是包装函数应该返回的C 返回类型。它后面
是作为函数参数的C 类型的变量列表。与qsort()的comparator 参数兼容的函数类型
是这个样子,如下所示:
CMPFUNC = ctypes.CFUNCTYPE(

返回类型

ctypes.c_int,

第一个参数的类型

ctypes.POINTER(ctypes.c_int),

第二个参数的类型

ctypes.POINTER(ctypes.c_int),
)
CFUNCTYPE()使用cdecl 调用约定,因此它只与CDLL 和
PyDLL共享库兼容。在Windows 上使用WinDLL或OleDLL
加载的动态库使用stdcall 调用约定。这意味着必须使
用其他工厂把Python 函数包装为C 可调用函数指针。在
ctypes 中,WINFUNCTYPE()可以完成这个包装过程。
把所有事情总结一下,假设我们要从标准C 库中用一个qsort()函数对一个随机乱序
的整数数列进行排序。以下是示例脚本,展示如何使用我们至今为止所学习的ctypes:
from random import shuffle
import ctypes
from ctypes.util import find_library
libc = ctypes.cdll.LoadLibrary(find_library(‘c’))
CMPFUNC = ctypes.CFUNCTYPE(

返回类型

ctypes.c_int,

第一个参数的类型

ctypes.POINTER(ctypes.c_int),

第二个参数的类型

ctypes.POINTER(ctypes.c_int),
)
def ctypes_int_compare(a, b):

参数是指针类型,所以我们可以通过[0]索引访问

print(" %s cmp %s" % (a[0], b[0]))

根据快速排序的规范,应该这样返回:

* 如果a 小于b,则小于0

* 如果a 等于b,则等于0

* 如果a 大于b,则大于0

return a[0] - b[0]
def main():
numbers = list(range(5))
shuffle(numbers)
print("shuffled: ", numbers)

创建一个代表数组的新类型

它和numbers 列表有相同的长度

NumbersArray = ctypes.c_int * len(numbers)

使用新类型创建一个新的C 数组

c_array = NumbersArray(*numbers)
libc.qsort(

指向被排序的数组的指针

c_array,

数组长度

len(c_array),

数组中单个元素的大小

ctypes.sizeof(ctypes.c_int),

回调(指向C 比较函数的指针)

CMPFUNC(ctypes_int_compare)
)
print("sorted: ", list(c_array))
if name == “main”:
main()
作为回调的比较函数有一个额外的print 语句,因此我们可以看到它在排序过程中是
如何执行的,如下所示:
$ python ctypes_qsort.py
shuffled: [4, 3, 0, 1, 2]
4 cmp 3
4 cmp 0
3 cmp 0
4 cmp 1
3 cmp 1
0 cmp 1
4 cmp 2
3 cmp 2
1 cmp 2
sorted: [0, 1, 2, 3, 4]

Logo

GitCode 天启AI是一款由 GitCode 团队打造的智能助手,基于先进的LLM(大语言模型)与多智能体 Agent 技术构建,致力于为用户提供高效、智能、多模态的创作与开发支持。它不仅支持自然语言对话,还具备处理文件、生成 PPT、撰写分析报告、开发 Web 应用等多项能力,真正做到“一句话,让 Al帮你完成复杂任务”。

更多推荐