目錄
1. 迭代(iteration)與迭代器(iterator)
1.1 構(gòu)建簡單迭代器
1.2 調(diào)用next()
1.3 迭代器狀態(tài)圖
2. 生成器(generator)
2.1 創(chuàng)建簡單生成器
2.2 利用函數(shù)定義生成器
3. 協(xié)程
3.1 概念理解
3.2 實例
4. 異步IO
4.1 概念理解
4.2 實例
1 迭代(iteration)與迭代器(iterator)
迭代是重復反饋過程的活動,其目的通常是為了接近并到達所需的目標或結(jié)果。每一次對過程的重復被稱為一次“迭代”社裆,而每一次迭代得到的結(jié)果會被用來作為下一次迭代的初始值螺戳。(維基百科)
iterator是實現(xiàn)了iterator.__iter__()和iterator.__next__()方法的對象iterator.__iter__()方法返回的是iterator對象本身衷畦。
1.1 構(gòu)建簡單迭代器
In [146]: test_iter = iter([i for i in range(1,4)])
In [147]: test_iter
Out[147]: <list_iterator at 0x84002a1f60>
返回列表迭代器對象关噪,實際上實現(xiàn)了iterator.__iter__()。
1.2 調(diào)用next()
In [148]: next(test_iter)
Out[148]: 1
In [149]: next(test_iter)
Out[149]: 2
In [150]: next(test_iter)
Out[150]: 3
In [151]: next(test_iter)
Traceback (most recent call last):
File "<ipython-input-151-ca50863582b2>", line 1, in <module>
next(test_iter)
StopIteration
In [152]:
可以看出next()實際調(diào)用了iterator.__next__()方法叶圃,每次調(diào)用更新iterator狀態(tài)宴抚,令其指向后一項勒魔,以便下一次調(diào)用并返回當前結(jié)果甫煞。
1.3 迭代器狀態(tài)圖
實際上迭代器就是實現(xiàn)迭代功能,先初始化迭代器冠绢,利用next()方法實現(xiàn)重復調(diào)用更新值抚吠,上次的終值時本次的初值。
2 生成器(generator)
通常帶有yield的函數(shù)便稱為生成器弟胀,yield是生成器執(zhí)行的暫涂Γ恢復點,也是實現(xiàn)generator的__next__()方法的關鍵孵户!可以對yield表達式進行賦值萧朝,也可以將yield表達式的值返回。簡而言之夏哭,generator是以更優(yōu)雅的方式實現(xiàn)的iterator剪勿。
2.1 創(chuàng)建簡單生成器
其創(chuàng)建方法區(qū)別于列表創(chuàng)建方式,在此采用()
而非[]
In [163]: test_gene = (x * x for x in range(1,4))
In [164]: test_gene
Out[164]: <generator object <genexpr> at 0x00000084002AD8E0>
In [166]: test_gene.__next__()
Out[166]: 1
In [167]: test_gene.__next__()
Out[167]: 4
In [168]: test_gene.__next__()
Out[168]: 9
In [169]: test_gene.__next__()
Traceback (most recent call last):
File "<ipython-input-169-e6166353d257>", line 1, in <module>
test_gene.__next__()
StopIteration
2.2 利用函數(shù)定義生成器
In [173]: def test_gene(a):
...: print("第一步")
...: yield a
...: a += 1
...: print("第二步")
...: yield a
...: a += 1
...: print("第三步")
...: yield a
...: a += 1
...:
...:
...: g = test_gene(1)
In [174]: g
Out[174]: <generator object test_gene at 0x0000008400295620>
In [175]: g.__next__()
第一步
Out[175]: 1
In [176]: g.__next__()
第二步
Out[176]: 2
In [177]: g.__next__()
第三步
Out[177]: 3
In [178]: g.__next__()
Traceback (most recent call last):
File "<ipython-input-178-60e4a84be5d7>", line 1, in <module>
g.__next__()
StopIteration
可以看出如果一個函數(shù)定義中包含yield關鍵字方庭,那么這個函數(shù)就不再是一個普通函數(shù),而是一個generator酱固。在每次調(diào)用next()的時候執(zhí)行:
- 遇到y(tǒng)ield語句返回械念;
- 保留上下文環(huán)境(保留局部變量狀態(tài));
- 再次執(zhí)行時從上次返回的yield語句處繼續(xù)執(zhí)行运悲。
總的來說生成器是一類特殊迭代器龄减,一個產(chǎn)生值的函數(shù) yield 是一種產(chǎn)生一個迭代器卻不需要構(gòu)建迭代器的精密小巧的方法。很明顯可以看出生成器(Generator)是采用邊循環(huán)邊計算的機制班眯,當我們只需訪問一個大列表的前幾個元素的情況下可以不必創(chuàng)建完整的list希停,從而節(jié)省大量的空間。
3 協(xié)程
3.1 概念理解
線程與進程署隘,有自己的上下文宠能,調(diào)度是由CPU來決定調(diào)度的;而協(xié)程也相對獨立磁餐,有自己的上下文违崇,但是其切換由自己控制,由當前協(xié)程切換到其他協(xié)程由當前協(xié)程來控制(程序員控制)诊霹,其實就是在一個線程中切換子線程羞延。
相比多線程有如下好處:一是協(xié)程極高的執(zhí)行效率。因為子程序切換不是線程切換脾还,而是由程序自身控制伴箩,因此,沒有線程切換的開銷鄙漏,當線程數(shù)量越多嗤谚,協(xié)程的性能優(yōu)勢就越明顯棺蛛。二是不需要多線程的鎖機制,因為只有一個線程呵恢,也不存在同時寫變量沖突鞠值,在協(xié)程中控制共享資源不加鎖,只需要判斷狀態(tài)就好了渗钉,所以執(zhí)行效率比多線程高很多彤恶。
協(xié)程、線程鳄橘、進程在不同場景下的適用性不盡相同声离,在其他語言中,協(xié)程的其實是意義不大的多線程即可已解決I/O的問題瘫怜,但是在python因為有GIL(Global Interpreter Lock 全局解釋器鎖 )在同一時間只有一個線程在工作术徊,所以如果一個線程里面I/O操作特別多,協(xié)程就比較適用鲸湃,如網(wǎng)絡請求赠涮。
3.2 實例
Python中的協(xié)程是通過“生成器(generator)”的概念實現(xiàn)的。這里引用廖雪峰Python教程中的例子暗挑,并將其修改為定外賣場景:
def shop():
'''定義商家(生成器)
'''
print("[-商家-] 開始接單 ......")
print("###############################")
r = "商家第1次接單完成" # 初始化返回結(jié)果笋除,并在啟動商家時,返回給消費者
while True:
n = yield r # (n = yield):商家通過yield接收消費者的消息炸裆,(yield r):返給結(jié)果
print("[-商家-] 正在處理第%s次訂單 ......" % n)
print("[-商家-] 第%s次訂單正在配送中 ......" % n)
print("[-商家-] 第%s次訂單已送達" % n)
r = "商家第%s次接單完成" % (n+1) # 商家信息垃它,下個循環(huán)返回給消費者
def consumer(g):
'''定義消費者
@g:商家生成器
'''
print("[消費者] 開始下單 ......")
r = g.send(None) # 啟動商家生成器
n = 0
while n < 5:
n += 1
print("[消費者] 已下第%s單" % n)
print("[消費者] 接受商家消息:%s" % r)
r = g.send(n) # 向商家發(fā)送下單消息并準備接收結(jié)果。此時會切換到消費者執(zhí)行
print("###############################")
g.close() # 關閉商家生成器
print("[消費者] 停止接單 ......")
if __name__ == "__main__":
g = shop()
consumer(g)
[消費者] 開始下單 ......
[-商家-] 開始接單 ......
###############################
[消費者] 已下第1單
[消費者] 接受商家消息:商家第1次接單完成
[-商家-] 正在處理第1次訂單 ......
[-商家-] 第1次訂單正在配送中 ......
[-商家-] 第1次訂單已送達
###############################
[消費者] 已下第2單
[消費者] 接受商家消息:商家第2次接單完成
[-商家-] 正在處理第2次訂單 ......
[-商家-] 第2次訂單正在配送中 ......
[-商家-] 第2次訂單已送達
###############################
[消費者] 已下第3單
[消費者] 接受商家消息:商家第3次接單完成
[-商家-] 正在處理第3次訂單 ......
[-商家-] 第3次訂單正在配送中 ......
[-商家-] 第3次訂單已送達
###############################
[消費者] 已下第4單
[消費者] 接受商家消息:商家第4次接單完成
[-商家-] 正在處理第4次訂單 ......
[-商家-] 第4次訂單正在配送中 ......
[-商家-] 第4次訂單已送達
###############################
[消費者] 已下第5單
[消費者] 接受商家消息:商家第5次接單完成
[-商家-] 正在處理第5次訂單 ......
[-商家-] 第5次訂單正在配送中 ......
[-商家-] 第5次訂單已送達
###############################
[消費者] 停止接單 ......
4 異步IO實例
4.1 概念理解
異步是區(qū)別于同步烹看,這里的同步指的并不是所有線程同時進行国拇,而是所有線程在時間軸上有序進行。在實際的IO操作的過程中惯殊,當前線程被掛起酱吝,而其他需要CPU執(zhí)行的代碼就無法被當前線程執(zhí)行了。異步正是為解決CPU高速執(zhí)行能力和IO設備的龜速嚴重不匹配土思,當代碼需要執(zhí)行一個耗時的IO操作時掉瞳,它只發(fā)出IO指令,并不等待IO結(jié)果浪漠,然后就去執(zhí)行其他代碼了陕习。一段時間后,當IO返回結(jié)果時址愿,再通知CPU進行處理该镣。
異步IO是基于CPU與IO處理速度不一致并為了充分利用資源的方法之一,在上一篇《Python知識(1)——并發(fā)編程》中記錄到的多線程與多進程也是該問題的處理方法之一响谓。
4.2 實例
只有協(xié)程還不夠损合,還不足以實現(xiàn)異步IO省艳,我們必須實現(xiàn)消息循環(huán)和狀態(tài)的控制,在此我們先了解一下幾個關鍵詞嫁审。
asyncio
Python 3.4版本引入的標準庫跋炕,直接內(nèi)置了對異步IO的支持。asyncio的編程模型就是一個消息循環(huán)律适。我們從asyncio模塊中直接獲取一個EventLoop的引用辐烂,然后把需要執(zhí)行的協(xié)程扔到EventLoop中執(zhí)行,就實現(xiàn)了異步IO捂贿。async/await
python3.5中新加入的特性纠修, 將異步從原來的yield 寫法中解放出來,變得更加直觀厂僧。其中async修飾的函數(shù)為異步函數(shù)扣草,await 替換了yield from, 表示這一步為異步操作。aiohttp
一個提供異步web服務的庫颜屠,分為服務器端和客戶端辰妙。這里主要使用其客戶端。
import asyncio
import aiohttp
async def get(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
print(url, resp.status)
print(url, await resp.text())
loop = asyncio.get_event_loop() # 得到一個事件循環(huán)模型
urls = ["https://movie.douban.com/tag/科幻?start="+str(1)+"&type=T" for i in range(1,4)]
tasks = [ get(url) for url in urls] # 初始化任務列表
loop.run_until_complete(asyncio.wait(tasks)) # 執(zhí)行任務
loop.close() # 關閉事件循環(huán)列表
參考與拓展閱讀:
[1]Python生成器詳解 | 投稿
[2]廖雪峰Python教程
[3]Python學習:異步IO:協(xié)程和asyncio
[4]Python【第十篇】協(xié)程甫窟、異步IO
[5]Python進階:理解Python中的異步IO和協(xié)程(Coroutine)上岗,并應用在爬蟲中
[6]異步爬蟲: async/await 與 aiohttp的使用,以及例子
[7]Python 異步網(wǎng)絡爬蟲(1)