摘要:理解什么是回调,回调函数就应该是一个模板框架,也像是一个抽像流程,也像面向对象中的模块模式设计,也像wiki说那样,允许低层代码调用高层代码;根据这样思想,应用python进行实现,实现的方法有多种,分别为: 简单回调,基于闭包的回调,基于类的回调, 基于生成器的回调,采用队列管理的回调,内联回调等5个方法去实现相关的回调。
环境:
Python 3.5.2 (v3.5.2:4def2a2901a5, Jun 25 2016, 22:18:55) [MSC v.1900 64 bit (AMD64)] on win32
这个是接着上一篇《python[变量作用域-函数-闭包-装饰器-生成器]》写下来的。
0. 回调
对于回调,首先看看wiki的解释:在计算机程序设计中,回调函数,或简称回调(Callback 即call then back 被主函数调用运算后会返回主函数),是指通过函数参数(英语:Parameter (computer programming))传递到其它代码的,某一块可执行代码的引用。某一块可执行代码的引用。这一设计允许了底层代码调用在高层定义的子程序。《https://zh.wikipedia.org/wiki/%E5%9B%9E%E8%B0%83%E5%87%BD%E6%95%B0》
理解一下:主函数把自己的代码段的引用以函数数参数的形式转给了一个它调用的函数,让那个函数在适当的时候根据这个引用调用自己的代码段。
其实,回调是一个思想,看上图,那个Software library实现的函数就应该是一个模板框架,定义好一个流程,也像是一个抽像流程,也像面向对象中的模块模式设计,也像wiki说那样,允许低层代码调用高层代码。
既然是思想来的,就不限语言,当然强大的python也是可以实现的。
1. 简单回调
回调函数是单个函数,就是用来显示数据那个。
from functools import wraps
from queue import Queue
def foo(func, args, *, callback):
# 计算第一个函数结果
result = func(*args)
# 调用回调函数
callback(result)
def bar(x, y):
return x + y
print('----------demo01:simple------------')
def show_data(data):
print(data)
# 两个数相加,并显示出来
foo(bar, (1, 2), callback=show_data)
foo(bar, (10, 2), callback=show_data)
结果
----------demo01:simple------------
3
12
2. 基于闭包的回调
闭包的回调注意那个自由变量,就像是一个静态变量一样,看后面的结果。
print('----------demo02:callback with extra informatin------------')
# 采用闭包来满足这个需求
def foo02():
i = 0
j = 99
n = 3 # n不是自由变量
def handler(v):
nonlocal i # 闭包:这个i是外部函数的,要加上nonlocal;这个i对于这个内部函数来说是一个静态的东西了
i += 1
nonlocal j
print('[i(%d)-j(%d)] : %s' % (i, j, v))
return handler
f02 = foo02()
# 这个可以看到这个闭包是有一个自由变量,也就是在函数还未调用就有了,就像是静态变量。
for i, c in enumerate(f02.__closure__):
print('NO.%d:%s' % (i, c.cell_contents))
foo(bar, (1, 2), callback=f02)
for i, c in enumerate(f02.__closure__):
print('NO.%d:%s' % (i, c.cell_contents))
foo(bar, (10, 2), callback=f02)
for i, c in enumerate(f02.__closure__):
print('NO.%d:%s' % (i, c.cell_contents))
结果
----------demo02:callback with extra informatin------------
NO.0:0
NO.1:99
[i(1)-j(99)] : 3
NO.0:1
NO.1:99
[i(2)-j(99)] : 12
NO.0:2
NO.1:99
3. 基于类的回调
print('----------demo03:callback with 类------------')
# 一般闭包可以实现的,类都可以实现,本质上,函数也是一个类对象的,一点不怀疑类的实现回调的能力,
# 其它语言例如java对于有窗口的,在监听事件的时候是实现一个接口的对象的。
class cc(object):
def __init__(self):
self.i = 0
def foo03(self, value):
self.i += 1
print('[%d]:%s' % (self.i, value))
obj = cc()
foo(bar, (1, 2), callback=obj.foo03)
foo(bar, (10, 2), callback=obj.foo03)
结果
----------demo03:callback with 类------------
[1]:3
[2]:12
4. 基于生成器的回调
print('----------demo04:callback with 生成器------------')
# 生成器可以很优雅地去实现回调,不采用内部函数了
def foo04():
i = 0
while True:
result = yield
i += 1
print('[%d]:%s' % (i, result))
g = foo04()
next(g) # 记得这个用来启动生成器的,也可以用g.send(None)
foo(bar, (1, 2), callback=g.send)
foo(bar, (10, 2), callback=g.send)
结果:
----------demo04:callback with 生成器------------
[1]:3
[2]:12
5. 采用队列管理的回调
print('----------demo05:callback with 生成器 改进[采用队列管理,让代码更紧凑] ------------')
# 当我们的设计的回调,不只是单业务了,是多业务了,是一
def bar01(a, b):
return max(a, b)
def bar02(a, b):
return min(a, b)
def bar03(a, b):
return a - b
def foo05():
print('Begin')
r = yield (bar, (2, 3))
print(r)
r = yield (bar01, (2, 3))
print(r)
r = yield (bar02, (2, 3))
print(r)
print('Goodbye')
# 这里面实现了控制yeild与send的管理流程
f = foo05()
result_queue = Queue()
# 初始一个None,主要是用来启动生成器,
result_queue.put(None)
while True:
try:
a = f.send(result_queue.get())
foo(a[0], a[1], callback=result_queue.put)
except StopIteration:
break
结果
----------demo05:callback with 生成器 改进[采用队列管理,让代码更紧凑] ------------
Begin
5
3
2
Goodbye
6. 内联回调
print('----------demo06:callback with 生成器 改进[采用装饰器,抽取模块,让代码更简单,内联回调] ------------')
def inlined_backcall(func):
@wraps(func)
def wrapper(*args):
#这里是生成器,不是函数调用,不要傻傻在调试时发出为什么函数没有调用的问题
f = func(*args)
result_queue = Queue()
result_queue.put(None)
while True:
result = result_queue.get()
try:
a = f.send(result)
foo(a[0], a[1], callback=result_queue.put)
except StopIteration as e:
break
return wrapper
@inlined_backcall
def foo06():
print('Begin')
r = yield (bar, (2, 3))
print(r)
r = yield (bar01, (2, 3))
print(r)
r = yield (bar02, (2, 3))
print(r)
print('Goodbye')
foo06()
结果
----------demo06:callback with 生成器 改进[采用装饰器,抽取模块,让代码更简单,内联回调] ------------
Begin
5
3
2
Goodbye
参考:
《回调函数(callback)是什么?》https://www.zhihu.com/question/19801131
【作者:happyprince, http://blog.csdn.net/ld326/article/details/78760772】