一日一技:在 Python 中實(shí)現(xiàn)延遲調(diào)用
熟悉 Golang 的同學(xué)都知道,Golang 里面有一個(gè)關(guān)鍵詞叫做defer,它可以實(shí)現(xiàn)延遲調(diào)用。
實(shí)際上在 Python 里面也有相關(guān)的語法,那就是contextlib.ExitStack。
我們來看這樣一個(gè)場景:
我有一個(gè)函數(shù)parse,它的作用是從 Redis 中持續(xù)讀入數(shù)據(jù),并寫入到MongoDB 中。示例代碼如下:
- import json
- import redis
- import pymongo
- client = redis.Redis()
- handler = pymongo.MongoClient().test.data
- def parse():
- data = client.lpop('test')
- if not data:
- return
- handler.insert_one(json.loads(data))
但現(xiàn)在我想增加一個(gè)需求,當(dāng)Redis 讀取結(jié)束或者讀取數(shù)據(jù)報(bào)錯(cuò)的時(shí)候,能把當(dāng)前的時(shí)間也寫入到MongoDB 中。
那么代碼可能變成下面這樣:
- import json
- import redis
- import datetime
- import pymongo
- client = redis.Redis()
- handler = pymongo.MongoClient().test.data
- def parse():
- while True:
- try:
- data = client.lpop('test')
- if not data:
- handler.insert_one({'finished': True, 'ts': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')})
- return
- handler.insert_one(json.loads(data))
- except Exception:
- handler.insert_one({'finished': True, 'ts': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'})
可以看到,代碼變得很難看了。
現(xiàn)在,我們可以使用延遲調(diào)用來讓代碼變得更好看。
要實(shí)現(xiàn)這個(gè)目的,就可以開始使用ExitStack了。它可以注冊多個(gè)回調(diào)函數(shù),在退出上下文縮進(jìn)時(shí)執(zhí)行。
我們先來看一個(gè)簡單的例子:
- import contextlib
- def callback_1():
- print('我是第一個(gè)回調(diào)函數(shù)')
- def callback_2(x):
- print(f'我是第二個(gè)回調(diào)函數(shù),傳入?yún)?shù):{x}')
- with contextlib.ExitStack() as stack:
- stack.callback(callback_1)
- stack.callback(callback_2, 100)
- print(12345)
- print('xxxx')
- print('退出縮進(jìn)')
運(yùn)行效果如下圖所示:
可以看出以下特點(diǎn):
- 被添加的回調(diào)函數(shù)進(jìn)入了一個(gè)棧,所以后添加的回調(diào)函數(shù)先調(diào)用
- 回調(diào)函數(shù)會(huì)在結(jié)束縮進(jìn)的時(shí)候被調(diào)用
現(xiàn)在我們來人工構(gòu)造一個(gè)異常:
可以看到,即使縮進(jìn)里面出現(xiàn)了報(bào)錯(cuò),回調(diào)函數(shù)仍然可以正常運(yùn)行。等所有回調(diào)函數(shù)運(yùn)行完成以后,Python 才會(huì)退出。
基于以上特點(diǎn),我們就可以來重構(gòu)最開始的代碼了:
- import json
- import redis
- import datetime
- import pymongo
- import contextlib
- client = redis.Redis()
- handler = pymongo.MongoClient().test.data
- def add_ts():
- handler.insert_one({'finished': True, 'ts': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')})
- def parse():
- with contextlib.ExitStack() as stack:
- stack.callback(add_ts)
- while True:
- data = client.lpop('test')
- if not data:
- return
- handler.insert_one(json.loads(data))
無論是正常運(yùn)行結(jié)束還是運(yùn)行過程中報(bào)錯(cuò),add_ts函數(shù)都會(huì)正常運(yùn)行,確保始終增加一條日期數(shù)據(jù)。
本文轉(zhuǎn)載自微信公眾號(hào)「未聞Code」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系未聞Code公眾號(hào)。