應(yīng)用場(chǎng)景:
- 文件的讀寫
- 數(shù)據(jù)庫(kù)的讀寫操作
- Flask的上下文管理
上下文管理協(xié)議:當(dāng)使用with語句時(shí)擒抛,解釋器會(huì)自動(dòng)調(diào)用 __enter__
,__exit__
class Sample:
def __enter__(self):
print('enter') #進(jìn)入資源
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print('exit') #釋放資源
def do_something(self):
print('do something')
with Sample() as sample:
sample.do_something()
輸出
enter
do something
exit
進(jìn)入with語句,調(diào)用__enter__
补疑;退出with語句歧沪,調(diào)用__exit__
事實(shí)上sample并不是sample=Sample(),而是__enter__
返回的對(duì)象癣丧,即如果__enter__
沒有return槽畔,sample將為None。
exc_type:異常類型胁编;exc_val:異常值厢钧;exc_tb:traceback。如果with語句中有異常發(fā)生嬉橙,__exit__
中會(huì)收集這些異常信息早直。
async with
async with的用法和with一樣,只是內(nèi)部使用__aenter__
和__aexit__
來定義上下文市框。這樣我們就能在上下文中使用異步編程霞扬。
class Lock(object):
def __init__(self, redis_client, key="", ttl=60, timeout=30, interval=0.03, is_wait=True):
"""
:param key:
:param ttl: expire time(鎖最大過期時(shí)間) / s
:param timeout: timeout(阻塞最大時(shí)間) / s
:param interval: sleep interval(睡眠間隔) / s
:return:
Usage::
with Lock('my_lock') as lock:
pass
"""
self.redis_client = redis_client
self.key = f"redis:lock:{key}"
self.ttl = ttl
self.timeout = timeout
self.interval = interval
self.value = uuid.uuid1().hex
self.is_wait = is_wait
async def __aenter__(self):
res, wait_times = await self.async_acquire()
return wait_times
async def __aexit__(self, exc_type, exc, tb):
self.release()
async def async_acquire(self):
timeout = self.timeout
wait_times = 0 # 嘗試獲取鎖的次數(shù)
if timeout == 0:
while True:
wait_times += 1
# redis操作理論上也需要改為異步,由于redis操作很快枫振,暫時(shí)先用同步
if self.redis_client.set(self.key, self.value, ex=self.ttl, nx=True):
return True, wait_times
if not self.is_wait: # 不等待鎖
raise TooFrequentException('Operation is too frequent')
await asyncio.sleep(self.interval)
else:
while timeout >= 0:
wait_times += 1
if self.redis_client.set(self.key, self.value, ex=self.ttl, nx=True):
return True, wait_times
timeout -= self.interval
if not self.is_wait: # 不等待鎖
raise TooFrequentException('Operation is too frequent')
await asyncio.sleep(self.interval)
raise LockTimeout("Timeout whilst waiting for lock")