問題: 希望編寫回調(diào)函數(shù)可以攜帶額外的狀態(tài)以便在回調(diào)函數(shù)內(nèi)部使用
首先定義一個回調(diào)函數(shù):
def apply_async(func, args, *, callback):
result = func(*args)
callback(result)
使用示例:
def print_reqult(result):
""" 僅接受一個單獨的函數(shù) """
print('Got:', result)
def add(x, y):
return x + y
apply_async(add, (1, 2), callback=print_reqult)
Got: 3
有些時候我們希望回調(diào)函數(shù)可以同其他變量或者部分環(huán)境進行交互迟螺。
解決方案:一種在回調(diào)函數(shù)中攜帶額外信息的方法是使用綁定方法而不是普通的函數(shù)
方案1:比如柔滔,下面這個類保存了一個內(nèi)部序列號碼,每當(dāng)接收到一個結(jié)果時就遞增這個號碼
class ResultHanlder:
def __init__(self):
self.sequence = 0
def handler(self, result):
self.sequence +=1
print('[{}] Got: {}'.format(self.sequence, result))
r = ResultHanlder()
apply_async(add, (1, 2), callback=r.handler)
[1] Got: 3
方案2:作為類的替代方案,也可以使用閉包來捕獲狀態(tài)
def make_result():
sequence = 0
def hanlder(result):
nonlocal sequence
sequence += 1
print('[{}] Got: {}'.format(sequence, result))
return hanlder
hanlder = make_result()
apply_async(add, (1, 2), callback=hanlder)
[1] Got: 3
方案3:有時候可以利用協(xié)程(coroutine)來完成同樣的任務(wù)
def make_result():
sequence = 0
while True:
result = yield
sequence += 1
print('[{}] Got: {}'.format(sequence, result))
# 對于協(xié)程來說李剖,可以使用它的send()方法來作為回調(diào)函數(shù)
hanlder = make_result()
next(hanlder) # 在使用協(xié)程前需要對其調(diào)用一次next()
apply_async(add, (1, 2), callback=hanlder.send)
[1] Got: 3
方案4:也可以通過額外的參數(shù)在回調(diào)函數(shù)中攜帶狀態(tài),然后使用partial()來處理參數(shù)個數(shù)的問題
class SequenceNo:
def __init__(self):
self.sequence = 0
def hanlder(result, seq):
seq.sequence += 1
print('[{}] Got: {}'.format(seq.sequence, result))
seq = SequenceNo()
from functools import partial
apply_async(add, (1, 2), callback=partial(hanlder, seq=seq))
[1] Got: 3
方案5:使用lambda表達式來實現(xiàn)上述相同的功能
apply_async(add, (1, 2), callback=lambda r: hanlder(r, seq))
[2] Got: 3
總結(jié):
1、主要有兩種方法可用于捕獲和攜帶狀態(tài):
- 在類實例上攜帶狀態(tài)(將狀態(tài)附加到綁定方法上)玻侥;
- 在閉包中攜帶狀態(tài);
2亿蒸、如果使用閉包凑兰,那么需要對可變變量多加留意。nonlocal聲明用來表示變量sequence是在回調(diào)函數(shù)中修改的边锁;
3姑食、使用協(xié)程的好處:
- 更加清晰;
- 變量可以自由的修改茅坛,不必?fù)?dān)心nonlocal聲明音半;
- 在使用協(xié)程前需要先對其調(diào)用一次next()。