global 和 nonlocal
b = '全局變量 global variable'def foo():a = '局部變量 local variable'# 在局部里面操作全局變量,需要加上聲明global bb = b + '!!!'print(b)foo()
# 全局變量 global variable!!!
b = '全局變量 global variable'def foo():a = '局部變量 local variable'# 在二級局部變量里面操作一級局部變量,則使用 nonlocal 進行聲明def inner_foo():nonlocal aa = a + '!!!'print(a)inner_foo()print(a)foo()
# 局部變量 local variable!!!
# 局部變量 local variable!!!
協程
概念
基本認識
- 協程不是計算機提供的,它由程序員認為創造
- 協程(Coroutine),也可以被稱為微線程,是一種用戶態內的上下文切換技術
- 簡單來說,就是通過一個線程實現代碼塊相互切換執行
實現協程的方式
- 通過第三方模塊
greenlet
- yield 關鍵字
- asyncio 裝飾器(Python 3.4)
- async、await 關鍵字(Python 3.5)【推薦】
實現
通過 greenlet 實現(了解)
pip install greenlet
from greenlet import greenletdef func1():print(1) # 第2步:輸出1gr2.switch() # 第3步:切換到 func2 函數print(2) # 第6步:輸出2gr2.switch() # 第7步:切換到 func2 函數,從上一次執行的位置繼續向后執行def func2():print(3) # 第4步:輸出 3gr1.switch() # 第5步:切換到 func1 函數,從上一次執行的位置繼續向后執行print(4) # 第8步:輸出4gr1 = greenlet(func1)
gr2 = greenlet(func2)gr1.switch() # 第1步:去執行 func1 函數
通過 yield 關鍵字(了解)
def func1():yield 1yield from func2()yield 2def func2():yield 3yield 4# 調用生成器函數,返回生成器對象
obj = func1()
print(obj)# for...in... 背后會不斷調用 next(obj) 來獲取 yield 產生的數據
for i in obj:print(i)# <generator object func1 at 0x0000025CB2A84E80>
# 1
# 3
# 4
# 2"""
從上面的輸出結果來看,我們通過 yield 實現了協程:先在func1中輸出1,然后切換到func2中輸出3,
接著繼續輸出4,最后又切換到func1中輸出2
"""
通過 asyncio 模塊(了解)
- 在 Python 3.4 版本中引入了
asyncio
模塊
import asyncio@asyncio.coroutine
def func1():print('正在發送請求下載圖片A')yield from asyncio.sleep(2) # 遇到IO耗時操作,自動切換到task_list中的其他任務print('圖片A下載完成')@asyncio.coroutine
def func2():print('正在發送請求下載圖片B')yield from asyncio.sleep(2) # 遇到IO耗時操作,自動切換到task_list中的其他任務print('圖片B下載完成')task_list = [asyncio.ensure_future(func1()),asyncio.ensure_future(func2()),
]# 創建一個事件循環對象
loop = asyncio.get_event_loop()# 讓事件循環對象,執行"任務列表"里面的任務
loop.run_until_complete(asyncio.wait(task_list))
通過 async 和 await 關鍵字(掌握)
- 在 Python 3.5 版本中引入了
async
和await
關鍵字
import asyncioasync def func1():print('正在發送請求下載圖片A')await asyncio.sleep(3) # 遇到IO耗時操作,自動切換到task_list中的其他任務print('圖片A下載完成')async def func2():print('正在發送請求下載圖片B')await asyncio.sleep(1) # 遇到IO耗時操作,自動切換到task_list中的其他任務print('圖片B下載完成')task_list = [asyncio.ensure_future(func1()),asyncio.ensure_future(func2()),
]loop = asyncio.get_event_loop() # 創建一個事件循環對象
loop.run_until_complete(asyncio.wait(task_list)) # 讓事件循環對象,執行"任務列表"里面的任務
# 在 Python 3.7 版本中,可以使用 asyncio.run() 來替代上面兩行代碼# 運行結果:
# 正在發送請求下載圖片A
# 正在發送請求下載圖片B
# 圖片B下載完成
# 圖片A下載完成
協程函數與協程對象
import asyncio
import time# 協程函數
async def func():print('執行中...')await asyncio.sleep(3)print('執行完畢!')# 協程對象
obj = func() # 注意:在創建協程對象時,函數內部代碼不會執行# 執行協程函數
# loop = asyncio.get_event_loop() # 創建一個事件循環對象
# loop.run_until_complete(obj) # 讓事件循環對象做一些任務
asyncio.run(obj) # 此行代碼可以替代上面兩行代碼
await
# await + 可等待對象(協程對象、Future對象、Task對象)import asyncioasync def foo1():print('開始執行 foo1 內部代碼')# 遇到IO操作時,掛起當前協程(任務),等IO操作完成之后再繼續往下執行# 當協程掛起時,事件循環可以去執行其他協程(任務)response = await foo2()print('IO請求結束,結果為', response)print('結束執行 foo1 內部代碼')async def foo2():print('Start foo2')await asyncio.sleep(2)print('End foo2')return 'OK'asyncio.run(foo1())# 開始執行 foo1 內部代碼
# Start foo2
# End foo2
# IO請求結束,結果為 OK
# 結束執行 foo1 內部代碼
import asyncioasync def foo1():print('開始執行 foo1 內部代碼')response = await foo2()print('IO請求結束,結果為', response)response = await foo2()print('IO請求結束,結果為', response)print('結束執行 foo1 內部代碼')async def foo2():print('Start foo2')await asyncio.sleep(2)print('End foo2')return 'OK'asyncio.run(foo1())# 開始執行 foo1 內部代碼
# Start foo2
# End foo2
# IO請求結束,結果為 OK
# Start foo2
# End foo2
# IO請求結束,結果為 OK
# 結束執行 foo1 內部代碼
task 對象
- 用于在事件循環中添加多個任務的
import asyncioasync def foo2(name: str):print(f'{name}開始執行')await asyncio.sleep(2)print(f'{name}結束執行')return f'{name} OK'async def foo1():print('foo1 開始')# 創建task對象,將當前執行func函數任務添加到事件循環中task1 = asyncio.create_task(foo2('task_1'))# 創建task對象,將當前執行func函數任務添加到事件循環中task2 = asyncio.create_task(foo2('task_2'))response1 = await task1print(response1)response2 = await task2print(response2)print('foo1 結束')asyncio.run(foo1()) # 通過協程函數創建一個協程對象,充當參數傳入asyncio.run()函數中# foo1 開始
# task_1開始執行
# task_2開始執行
# task_1結束執行
# task_2結束執行
# task_1 OK
# task_2 OK
# foo1 結束
import asyncioasync def foo2(name: str):print(f'{name}開始執行')await asyncio.sleep(2)print(f'{name}結束執行')return f'{name} OK'async def foo1():print('foo1 開始')task_list = [asyncio.create_task(foo2('task_1')),asyncio.create_task(foo2('task_2')),]return_value, pending = await asyncio.wait(task_list, timeout=2) # 默認 timeout=None。如果超時,pending里面會有一個"待處理的任務"對象print(return_value) # 返回一個集合print(pending) # 返回一個集合print('foo1 結束')asyncio.run(foo1())
future 對象
- task 繼承 future,task 對象內部 await 結果的處理基于 future 對象來的
import asyncioasync def set_after(future):await asyncio.sleep(2)future.set_result('666')async def main():# 獲取當前事件循環loop = asyncio.get_running_loop()# 創建一個任務(future對象),這個任務什么都不干future = loop.create_future()# 創建一個任務(task對象),綁定了set_after函數,函數內部在2s之后,會給future賦值# 即:手動設置future任務的最終結果,那么future就可以結束了await loop.create_task(set_after(future))# 等待任務最終結果,沒有結果會一直等待下去data = await futureprint(data) # 666asyncio.run(main())
異步迭代器
import asyncio
import randomclass Reader(object):"""自定義異步迭代器"""def __init__(self):self.count = 0def __aiter__(self):return selfasync def readline(self):await asyncio.sleep(random.randint(1, 5))self.count += 1if self.count == 100:return Nonereturn self.countasync def __anext__(self):val = await self.readline()if val is None:raise StopAsyncIterationreturn valasync def func():obj = Reader()async for i in obj: # 背后會不斷的調用 __anext__() 方法print(i)asyncio.run(func())