前言:
之前了解单方法类变为函数的方案后一直在想怎么进行应用;现在发现一个给函数(方法)增加状态信息的案例,同样来自python cook! codeMan奉上.
回调函数callback
回调函数就是一个被作为参数传递的函数!
1.定义一个回调函数;
2.提供函数实现的一方在初始化的时候,将回调函数的函数指针注册给调用者;
3.当特定的事件或条件发生的时候,调用者使用函数指针调用回调函数对事件进行处理。
Python没有指针这个说法一般都是说函数名.简单来说就是定义一个函数,然后将这个函数的函数名传递给另一个函数做参数,以这个参数命名的函数就是回调函数。
# 执行函数
def apply_async(*args, callback):
callback(args)
#回调函数
def print_result(args):
print('Got:', args)
if __name__ == "__mian__":
apply_async(1,2,"a","-", callback=print_result)
以上为回调函数的基本用法.回调函数用途超广业务方面:事件处理器、等待后台任务完成后回调;系统编程中:线程、进程和定时器中都用到了回调函数.
问题
print_result() 函数仅仅只接受一个参数 result .不能再传入其他信息.
而当你想让回调函数访问其他变量或者特定环境的变量值的时候就会遇到麻烦。
解决方案1-类实现:
使用一个绑定方法来代替一个简单函数
def apply_async(func, args, *, callback):
result = func(*args)
callback(result)
class ResultHandler:
def __init__(self):
self.sequence = 0
def handler(self, result):
self.sequence += 1
print('[{}] Got: {}'.format(self.sequence, result))
def add(x, y):
return x + y
if __name__ == "__main__":
r = ResultHandler()
apply_async(add, (2, 3), callback=r.handler) # Got: 5
apply_async(add, ('hello', 'world'), callback=r.handler) #Got: helloworld
解决方案2-闭包实现
作为类的替代,可以使用一个闭包捕获状态值.优雅版
def apply_async(func, args, *, callback):
result = func(*args)
callback(result)
def make_handler():
sequence = 0
def handler(result):
nonlocal sequence
sequence += 1
print('[{}] Got: {}'.format(sequence, result))
return handler
def add(x, y):
return x + y
if __name__ == "__main__":
handler = make_handler()
apply_async(add, (2, 3), callback=handler) # Got: 5
apply_async(add, ('hello', 'world'), callback=handler) #Got: helloworld
解决方案3-协程解决
def apply_async(func, args, *, callback):
result = func(*args)
callback(result)
def make_handler():
sequence = 0
while True:
result_new = yield
sequence += 1
print('[{}] Got: {}'.format(sequence, result_new))
def add(x, y):
return x + y
if __name__ == "__main__":
handler = make_handler()
next(handler) # Advance to the yield
apply_async(add, (2, 3), callback=handler.send) # Got: 5
apply_async(add, ('hello', 'world'), callback=handler.send) #Got: helloworld
通过handler的send把add的结果也就是result待会yield的位置赋值给result_new
结论:
三种方式的优缺点:
使用类的实例属性进行进行存取,需要手动记录代码略显冗余
使用闭包进行存取,所有使用到的变量都可自动捕获,代码优雅很多.不过需要注意的是内部函数访问外部函数变量时一定要nonlocal声明不然代码会报错
使用协程方式代码最为简洁一个函数搞定,不过需要对协程的工作原理理解清晰(它的逻辑就抽象很多).不然容易忘掉添加next()或者不理解send干了什么.
写在后面
作者说为何要这么做:
基于回调函数的软件通常都有可能变得非常复杂。一部分原因是回调函数通常会
跟请求执行代码断开。因此,请求执行和处理结果之间的执行环境实际上已经丢失了。
如果你想让回调函数连续执行多步操作,那你就必须去解决如何保存和恢复相关的状
态信息了。