一、yield使用簡析
yield是一個生成器generator,返回一個interable對象。
該對象具有next()方法,可以通過next()查看接下來的元素是什么。
1.interable對象 ,可以遍歷的對象,如: list,str,tuple,dict,file,xrange等。
2.yield的作用是什么?只是循環里面的獲取中間變量的一個方法,把想要的變量每次使用yield保存起來直至循環結束,循環結束得到了一個generator對象
3.為什么使用yield?使用yield,一個函數改寫成generator,便具有了迭代的能力,比起用類的實例保存狀態計算下一個需要迭代的值,代碼更加簡潔,執行流程十分簡單。
4.如何判斷yield的類型?
1
2
3
4
5
6
7
8
9
|
def fab( max ): n, a, b = 0 , 0 , 1 while n < max : yield b # 使用 yield # print b a, b = b, a + b n = n + 1 for n in fab( 5 ): print n |
fab不是generator,fab(5)是generator。
好比類的定義和類的實例的區別。
1
2
3
4
5
|
>>> import types >>> isinstance (fab, types.GeneratorType) False >>> isinstance (fab( 5 ), types.GeneratorType) True |
fab 是無法迭代的,而 fab(5) 是可迭代的。
1
2
3
4
5
|
>>> from collections import Iterable >>> isinstance (fab, Iterable) False >>> isinstance (fab( 5 ), Iterable) True |
5.yield在文件讀取的應用?
如果字節使用read()讀取一個文件,會導致不可預測的內存占用。好的方法是使用yield,固定長度的緩沖區來不斷讀取文件,生成讀文件的迭代的generator。
1
2
3
4
5
6
7
8
9
|
def read_file(fpath): BLOCK_SIZE = 1024 with open (fpath, 'rb' ) as f: while True : block = f.read(BLOCK_SIZE) if block: yield block else : return |
二、async和await的使用
1.什么是進程、協程、異步?
- 協程是什么?
一種用戶級輕量級的線程,擁有自己的寄存器上下文和棧。
協程切換時候,將寄存器和棧保存在其他地方,當返回的時候,恢復原先保存的寄存器上下文和棧。
- 為什么使用協程?
主流語言采用多線程并發,線程相關的概念是搶占式多任務,協程相關的協作式多任務。
不管是多進程還是多線程,每次阻塞、切換陷入系統調用。
CPU跑操作系統的調度程序,調度程序決定運行哪一個進程(線程)。
線程非常小心的處理同步問題,而協程完全不存在這個問題。
對于CPU而言,多協程是單線程,CPU不會考慮調度、切換上下文,省去CPU的切換開銷。協程好于多線程的原因。
- 如何使用協程?
多進程+協程下,避開了CPU切換的開銷,又能把多個CPU充分利用起來,這種方式對于數據量較大的爬蟲還有文件讀寫之類的效率提升是巨大的。
2.如何處理200W數量的url,把所有的url保存下來?
- 單進程+單線程
- 單進程+多線程:開十個線程,速度不能提高十倍。線程的切換是有開銷的,無能無限的創建線程。
- 多進程+多線程:多進程的每個進程占用一個CPU,多線程一定程度上繞過了阻塞時間,所以相比單進程的多線程效率更高。
- 協程
3.使用async的await和gather
- await接受一個協程列表,返回done、pending兩個列表。done是已經完成的協程,pending是仍在跑的協程。通過.result()獲取完成的結果
- gather以gather(cro1, cro2, cro3, cro4…)的方式接受協程,返回的是一個結合了這么多個任務的協程。
async的使用:https://blog.csdn.net/qq_29785317/article/details/103294235
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
async def func1(num): print ( '--func1 start--' ) await asyncio.sleep(num) print ( '--func1 done--' ) return 'func1 ok' async def func2(num): print ( '--func2 start--' ) await asyncio.sleep(num) print ( '--func2 done--' ) return 'func2 ok' async def main(): task1 = asyncio.ensure_future(func1( 3 )) task2 = asyncio.ensure_future(func2( 5 )) tasks = [task1, task2] res = await asyncio.gather( * tasks) return res # done, pending = await asyncio.wait(tasks) # for t in done: # print(t.result()) # print(done) # print(pending) if __name__ = = '__main__' : loop = asyncio.get_event_loop() result = loop.run_until_complete(main()) print (result) ```python - - func1 start - - - - func2 start - - - - func1 done - - - - func2 done - - [ 'func1 ok' , 'func2 ok' ] |
三、協程的理解
1.協程的過程
協程中yield是控制流程的方式。
yield同接收器一樣,是一個生成器,需要先激活才能使用。
1
2
3
4
5
6
7
8
|
>>> def simple_corotine(): ... print ( '---->coroutine started' ) ... x = yield #有接收值,所以同生成器一樣,需要先激活,使用next ... print ( '---->coroutine recvied:' ,x) ... >>> my_coro = simple_corotine() >>> my_coro <generator object simple_corotine at 0x0000000000A8A518 > |
1
2
3
4
5
6
7
|
>>> next (my_coro) #先激活生成器,執行到yield val語句 #或者使用send(None)也可以激活生成器 - - - - >coroutine started >>> my_coro.send( 24 ) #向其中傳入值,x = yield - - - - >coroutine recvied: 24 Traceback (most recent call last): File "<stdin>" , line 1 , in <module> StopIteration #當生成器執行完畢時會報錯 |
協程在運行中的四種狀態
GEN_CREATE:等待開始執行
GEN_RUNNING:解釋器正在執行,這個狀態一般看不到
GEN_SUSPENDED:在yield表達式處暫停
GEN_CLOSED:執行結束
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
>>> def averager(): ... total = 0.0 ... count = 0 ... aver = None ... while True : ... term = yield aver ... total + = term ... count + = 1 ... aver = total / count ... >>> coro_avg = averager() >>> coro_avg.send( None ) >>> coro_avg.send( 10 ) 10.0 >>> coro_avg.send( 20 ) 15.0 >>> coro_avg.send( 30 ) 20.0 >>> coro_avg.send( 40 ) 25.0 |
每次循環結束在yield出暫停,直至下一個參數傳進來。
3.預激活協程的裝飾器(自定義激活的方式)
@裝飾器的作用是什么?裝飾原有的函數,給原油函數增加一個新的功能和方式。
為什么@可以實現裝飾器的功能?函數也是對象,函數可以作為實參傳給掐函數。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
>>> def coro_active(func): ... def inner( * args, * * kwargs): ... gen = func( * args, * * kwargs) ... next (gen) #gen.send(None) ... return gen ... return inner ... >>> @coro_active ... def averager(): ... total = 0.0 ... count = 0 ... aver = None ... while True : ... term = yield aver ... total + = term ... count + = 1 ... aver = total / count ... >>> coro_avg = averager() >>> coro_avg.send( 10 ) 10.0 >>> coro_avg.send( 20 ) 15.0 >>> coro_avg.send( 30 ) 20.0 |
4.終止協程和異常處理
當協程的next函數或者send函數發生錯誤的時候,協程就會終止掉。
需要創建異常捕捉對協程的異常情況進行處理,關閉當前協程。
5.讓協程返回值
yield使用方法 ??
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關注服務器之家的更多內容!
原文鏈接:https://blog.csdn.net/yue_yue0/article/details/118499351