首页 > 代码库 > 回调函数

回调函数

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数主要用来为不确定的事件、不确定的时间上进行的动作或响应。比如在C库里实现了一个算法叫做快速排序(qsort),为了能让库更加通用,不想在函数中嵌入排序逻辑,而让使用者来实现相应的逻辑,这时就可以使用回调函数方式来实现。如msvcrt.dll里的快速排序算法qsort, 我们现在来看一下这个例子,你就会很清楚它的作用:

#windows应用程序

from ctypes import *

from ctypes.wintypes import *

 

#取得快速排序的函数。

qsort = cdll.msvcrt.qsort;

qsort.restype = None

 

#定义排序比较函数

def py_cmp_func(a, b):

    print("py_cmp_func", a[0], b[0])

    return a[0] > b[0]

 

#转换为C的回调函数

CMPFUNC = CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))    

cmp_func = CMPFUNC(py_cmp_func)

 

#进行排序

num = 5

IntArray5 = c_int * num

ia = IntArray5(5, 1, 7, 33, 99)

 

#排序前输出数组

for i in ia:

    print(i, end = ‘ ‘)

    

print("\nqsort before")

qsort(ia, len(ia), sizeof(c_int), cmp_func)

 

#排序后输出数组

for i in ia:

    print(i, end = ‘ ‘)

运行这个例子之后输出如下:

5 1 7 33 99 

qsort before

py_cmp_func 1 5

py_cmp_func 7 5

py_cmp_func 33 7

py_cmp_func 99 33

py_cmp_func 1 5

py_cmp_func 7 5

py_cmp_func 33 7

py_cmp_func 1 5

py_cmp_func 7 5

py_cmp_func 1 5

1 5 7 33 99 

在这个例子里通过cdll.msvcrt.qsort来获取到msvcrt库的快速排序函数qsort,由于这个函数调用时给一个回调用函数来比较两个元素的大小。因而在后面定义了一个回调函数py_cmp_func,这个回调函数是Python的函数调用格式和类型,并不能直接把函数指针给qsort使用,必须通过ctypes转换才可以。为了这个目的,使用ctypes库里的CFUNCTYPE函数生成一个C语言的回调用函数cmp_func,这时才可以给qsort使用。在C里声明的回调函数的格式如下:

int compare( const void *arg1, const void *arg2 );

也就是说,这个声明CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))与上面这个声明是等效的。

通过运行这个例子之后,我们看到数组里的五个数字已经按从小到大排好顺序出来,达到了回调目的。如果想从大到小排序,只需要改写成下面这个函数:

def py_cmp_func(a, b):

    print("py_cmp_func", a[0], b[0])

    return a[0] < b[0]

跟原来的函数相比,这里只修改一行代码a[0] < b[0],可见回调函数的威力吧。其实这个例子里只是数值的比较,如果要进行字符串的长度排序,那么就可以修改这个比较函数内容为比较长度即可。如果想要比较所有字符的值加到一起,那个字符串值最大,也可以修改这个比较函数,把每个字符串的字符值累加到一起。

从这里看到以cdecl调用方式的转换函数CFUNCTYPE函数,而想转换为stdcall调用方式,就需要使用WINFUNCTYPE函数。比如声明一个Windows的窗口消息回调函数,就可以像这样编写:

WNDPROCTYPE = WINFUNCTYPE(c_int, HWND, c_uint, WPARAM, LPARAM)

这个函数的声明与下面这个回调函数的声明是一致的:

LRESULT CALLBACK WndProc(

HWND hwnd,

UINT uMsg,

WPARAM wParam,

LPARAM lParam

)

因此,在Python里经过WNDPROCTYPE对象的转换,就可以使用于WIN32API的回调函数里,完美地解决了回调函数的问题。