什么是回调函数?

我们绕点远路来回答这个问题。

编程分为两类:系统编程(system programming)和应用编程(application programming)。所谓系统编程,简单来说,就是编写库;而应用编程就是利用写好的各种库来编写具某种功用的程序,也就是应用。系统程序员会给自己写的库留下一些接口,即API(application programming interface,应用编程接口),以供应用程序员使用。所以在抽象层的图示里,库位于应用的底下。

当程序跑起来时,一般情况下,应用程序(application program)会时常通过API调用库里所预先备好的函数。但是有些库函数(library function)却要求应用先传给它一个函数,好在合适的时候调用,以完成目标任务。这个被传入的、后又被调用的函数就称为回调函数(callback function)。

打个比方,有一家旅馆提供叫醒服务,但是要求旅客自己决定叫醒的方法。可以是打客房电话,也可以是派服务员去敲门,睡得死怕耽误事的,还可以要求往自己头上浇盆水。这里,“叫醒”这个行为是旅馆提供的,相当于库函数,但是叫醒的方式是由旅客决定并告诉旅馆的,也就是回调函数。而旅客告诉旅馆怎么叫醒自己的动作,也就是把回调函数传入库函数的动作,称为登记回调函数(to register a callback function)。如下图所示(图片来源:维基百科):

可以看到,回调函数通常和应用处于同一抽象层(因为传入什么样的回调函数是在应用级别决定的)。而回调就成了一个高层调用底层,底层再回过头来调用高层的过程。(我认为)这应该是回调最早的应用之处,也是其得名如此的原因。

回调机制的优势

从上面的例子可以看出,回调机制提供了非常大的灵活性。请注意,从现在开始,我们把图中的库函数改称为中间函数了,这是因为回调并不仅仅用在应用和库之间。任何时候,只要想获得类似于上面情况的灵活性,都可以利用回调。

这种灵活性是怎么实现的呢?乍看起来,回调似乎只是函数间的调用,但仔细一琢磨,可以发现两者之间的一个关键的不同:在回调中,我们利用某种方式,把回调函数像参数一样传入中间函数。可以这么理解,在传入一个回调函数之前,中间函数是不完整的。换句话说,程序可以在运行时,通过登记不同的回调函数,来决定、改变中间函数的行为。这就比简单的函数调用要灵活太多了。请看下面这段Python写成的回调的简单示例:

even.py

#遇到问题没人解答?小编创建了一个Python学习交流QQ群:778463939
#寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
#回调函数1
#生成一个2k形式的偶数
def double(x):return x * 2#回调函数2
#生成一个4k形式的偶数
def quadruple(x):return x * 4`callback_demo.py`from even import *#中间函数
#接受一个生成偶数的函数作为参数
#返回一个奇数
def getOddNumber(k, getEvenNumber):return 1 + getEvenNumber(k)#起始函数,这里是程序的主函数
def main():    k = 1#当需要生成一个2k+1形式的奇数时i = getOddNumber(k, double)print(i)#当需要一个4k+1形式的奇数时i = getOddNumber(k, quadruple)print(i)#当需要一个8k+1形式的奇数时i = getOddNumber(k, lambda x: x * 8)print(i)if __name__ == "__main__":main()

运行callback_demp.py,输出如下:

3
5
9

上面的代码里,给getOddNumber传入不同的回调函数,它的表现也不同,这就是回调机制的优势所在。值得一提的是,上面的第三个回调函数是一个匿名函数。

易被忽略的第三方

通过上面的论述可知,中间函数和回调函数是回调的两个必要部分,不过人们往往忽略了回调里的第三位要角,就是中间函数的调用者。绝大多数情况下,这个调用者可以和程序的主函数等同起来,但为了表示区别,我这里把它称为起始函数(如上面的代码中注释所示)。

之所以特意强调这个第三方,是因为我在网上读相关文章时得到一种印象,很多人把它简单地理解为两个个体之间的来回调用。譬如,很多中文网页在解释“回调”(callback)时,都会提到这么一句话:“If you call me, I will call you back.”我没有查到这句英文的出处。我个人揣测,很多人把起始函数和回调函数看作为一体,大概有两个原因:

第一,可能是“回调”这一名字的误导;
第二,给中间函数传入什么样的回调函数,是在起始函数里决定的。实际上,回调并不是“你我”两方的互动,而是ABC的三方联动。有了这个清楚的概念,在自己的代码里实现回调时才不容易混淆出错。

另外,回调实际上有两种:阻塞式回调和延迟式回调。

两者的区别在于:阻塞式回调里,回调函数的调用一定发生在起始函数返回之前;而延迟式回调里,回调函数的调用有可能是在起始函数返回之后。这里不打算对这两个概率做更深入的讨论,之所以把它们提出来,也是为了说明强调起始函数的重要性。网上的很多文章,提到这两个概念时,只是笼统地说阻塞式回调发生在主调函数返回之前,却没有明确这个主调函数到底是起始函数还是中间函数,不免让人糊涂,所以这里特意说明一下。另外还请注意,本文中所举的示例均为阻塞式回调。延迟式回调通常牵扯到多线程。

在Python中的一些使用示例:

实例一

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:778463939
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
import random as rd# -----------被调用方----------------------------
def newRN(fn):  # 生成10个[0,1)之间小数ns = []for i in range(10):n = round(rd.random(), 2)ns.append(n)# 不用直接 return, 因为调用方 通知不接返回结果# 改成回调函数方式fn(ns)  # 调用是调用方函数,这一操作称之为回调# ----------------调用方------------------------# 定义回调函数
def abc(*args):# 进入到本函数内,意味着被调用方函数已执行完print('生成数据成功')print(args)
newRN(abc)

输出结果:

生成数据成功
([0.27, 0.39, 0.81, 0.95, 0.78, 0.34, 0.89, 0.14, 0.19, 0.05],)

实例二

我们来打个比方:

学校要进行出入管制了,告诉门卫发现宠物和车要上报(这个是回调函数注册),然后管理人员根据门卫的上报进行处理(这个是回调函数的执行)。

import os,sys
Find={'Type':'','Color':'','Size':''
}#定义汇报内容
def CallFun(cmd,Find):#回调函数的定义,在这里处理各种回调情况if cmd=='Type':if Find['Type']=='Dog' or Find['Type']=='Cat':print 'A Pet:'else:print 'A Transport:'elif cmd=='Print':print Findelse:print 'error'
def GiveInfo(i):#该段是填报信息,可忽略type0=['Dog','Cat']type1=['Car','Truck']color0=['Black','White','Pink']size0=['Big','Middle','Small']t0=i % 2if  t0== 0:Find['Type'] = type0[i%2]else:Find['Type'] = type1[i%2]Find['Color'] = color0[i%3]Find['Size'] = size0[i%3]
def FindObj(num,cmd,CallBackFun):#发现目标,启动回调函数GiveInfo(num)#门卫填报信息CallBackFun(cmd,Find)#启动回调函数
if __name__ == '__main__':cmds=['Type','Print','Try']for i in range(0,10):#定义十次上报print '----------%d-------------'%iFindObj(i,cmds[i%3],CallFun)#这里注册回调函数(就是告知门卫的过程)

回调利于模块解耦。

Python回调函数用法实例相关推荐

  1. python asyncio回调函数_python回调函数用法实例分析

    python回调函数用法实例分析 本文实例讲述了python回调函数用法.分享给大家供大家参考.具体分析如下: 软件模块之间总是存在着一定的接口,从调用方式上,可以把他们分为三类:同步调用.回调和异步 ...

  2. python回调函数实例详解_Python回调函数用法实例详解

    本文实例讲述了Python回调函数用法.分享给大家供大家参考.具体分析如下: 一.百度百科上对回调函数的解释: 回调函数就是一个通过函数指针调用的函数.如果你把函数的指针(地址)作为参数传递给另一个函 ...

  3. python的scatter函数_python scatter函数用法实例详解

    这篇文章主要介绍了python scatter函数用法实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 函数功能:寻找变量之间的关系. 调用签 ...

  4. python中max函数用法_Python中max函数用法实例分析

    Python中max函数用法实例分析 更新时间:2015年07月17日 15:45:09 作者:优雅先生 这篇文章主要介绍了Python中max函数用法,实例分析了Python中max函数的功能与使用 ...

  5. python中property函数_python 邮件表格Python中property函数用法实例分析

    本文实例讲述了Python中property函数用法.分享给大家供大家参考,具体如下: 通常我们在访问和赋值属性的时候,都是在直接和类(实例的)的__dict__打交道,或者跟数据描述符等在打交道.但 ...

  6. zip在python中的用法_Python中zip()函数用法实例教程

    本文实例讲述了Python中zip()函数的定义及用法,相信对于Python初学者有一定的借鉴价值.详情如下: 一.定义: zip([iterable, ...])zip()是Python的一个内建函 ...

  7. python中symbols函数用法_Python基础之函数用法实例详解

    本文以实例形式较为详细的讲述了Python函数的用法,对于初学Python的朋友有不错的借鉴价值.分享给大家供大家参考之用.具体分析如下: 通常来说,Python的函数是由一个新的语句编写,即def, ...

  8. python匿名函数里用if_python匿名函数用法实例分析

    本文实例讲述了python匿名函数用法.分享给大家供大家参考,具体如下: 匿名函数特点: 1.只能有一个表达式 2.不用return,默认return结果 3.不需要名字,防止函数名重复 4.是对象, ...

  9. python中zip()函数的用法_Python zip()函数用法实例分析

    本文实例讲述了python zip()函数用法.分享给大家供大家参考,具体如下: 这里介绍python中zip()函数的使用: >>> help(zip) Help on built ...

最新文章

  1. 全球顶级专家为你解读:什么是真正的 DevOps?
  2. 计算机网络【在一个1Mb/s的卫星信道上发送1000bit长的帧。确认总是捎带在数据帧中。帧头很短,使用3位的序列号。对以下协议而言,可以取得的最大信道利用率是多少?(a)停-等协议;(b)(c)】
  3. Android 高级Drawable资源---复合Drawable----变换Drawable
  4. 前端工程师应该达到什么水平,找工作薪资才比较高?
  5. MongoDB误删表恢复
  6. LeetCode 1215. 步进数(BFS/DFS)
  7. [Bzoj]5343: [Ctsc2018]混合果汁
  8. 音视频开发(20)---基于RTMP推送实时AAC+H264流(一)
  9. python urllib3 post ssl,python post https报错解决方案
  10. 速成pytorch学习——3天自动微分机制
  11. 美政府停摆或考验机场安检 携枪旅客曾无障碍登机
  12. php生成手写字,快速word手写字体在线生成器笔迹仿手写
  13. matlab 水箱fuzzy,matlab中使用fuzzy工具箱
  14. 遥感影像几何校正模型(RPC模型)
  15. 520送男朋友什么礼物最好、送男友礼物清单
  16. 实验五—JEPG 原理分析及 JPEG 解码器的调试
  17. RPG Maker MV 遇敌对战
  18. 10月各国最新签证及入境政策汇总
  19. 光纤跳线、尾纤、光纤连接器之间有什么区别
  20. 详解Pod和容器资源管理和分配(CPU和内存分配,临时存储管理)

热门文章

  1. 【Unity】2.11 了解游戏有哪些分类对你开阔思路有好处
  2. 技术开发人员适应其他部门提需求的一个经验
  3. ASP.NET实现微信功能(2)(服务号高级群发)
  4. Hsiaoyang: Google与站点地图Sitemap
  5. 菜鸟也玩WebMatrix
  6. armax函数 matlab 源码_将可读性不强的MATLAB公式样式转为直观的LaTeX样式
  7. 【IT人沟通技巧】如何学会结构化倾听
  8. 【转】后勤自动过帐(OBYC)详细配置说明
  9. PP模块入门考试试题,看看能得多少分吧!
  10. 【力荐】ABAP常用DEBUG调试代码的八种方式