Python 高手编程系列八百六十七:传递 Python 函数作为 C 回调

这是一个非常流行的设计模式,将函数实现的部分工作委托给用户提供的自定义回调。
在 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),
)
把所有事情总结一下,假设我们要从标准 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]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值