python隨用隨學20200221-生成器中的send(),throw()和close()方法

send()方法

文檔定義

generator.send(value)
Resumes the execution and “sends” a value into the generator function. The value argument becomes the result of the current yield expression. The send() method returns the next value yielded by the generator, or raises StopIteration if the generator exits without yielding another value. When send() is called to start the generator, it must be called with None as the argument, because there is no yield expression that could receive the value.

  • 這裏要注意send方法和next方法的區別,在於send方法會首先把上一次掛起的yield語句的返回值通過參數設定,從而實現和生成器方法的交互.
  • send(None)next()方法完全等價.

代碼示例

  1. def gen(): 

  2. x = yield 1 

  3. print('gen---{0}'.format(x)) 

  4. y = yield 2 

  5. print('gen---{0}'.format(y)) 

  6. z = yield 3 

  7. print('gen---{0}'.format(z)) 

  8.  

  9. g = gen() 

  10.  

  11. result0 = g.send(None) 

  12. print('main---{0}'.format(result0)) 

  13. result1 = g.send(111) 

  14. print('main---{0}'.format(result1)) 

  15. result2 = g.send(222) 

  16. print('main---{0}'.format(result2)) 

  17. result3 = g.send(333) 

  18. print('main---{0}'.format(result3)) 

  19.  

把上面那一段讀懂之後 這個結果就好理解了.


pic-1582723999405.png

  • 首先result0 = g.send(None)執行,激活生成器到yeild 1. 生成器返回1 輸出main---1
  • 第二步到result1 = g.send(111) 從上次掛起的地方向生成器傳遞111的值,也就是x=111 接着往下執行輸出gen---111
  • 往下執行到yeild 2,返回2 然後打印main---2,同理下一步到result2 = g.send(222)
  • y得到值222,輸出gen---222
  • 然後繼續執行到yeild 3,返回3,輸出main---3
  • 再下一步send(333)的時候,從上次終端的位置z=yield 3獲取輸入值,z被賦值爲333,打印gen---333
  • 往下沒有yield了,拋出StopIteration的異常.

剛開始沒搞明白是因爲沒有搞明白這個send進去的值在那裏接收. 這裏注意的是send進去的值在上一次yeild掛起的位置接收.

throw()方法

文檔定義

generator.throw(type[, value[, traceback]])
Raises an exception of type type at the point where the generator was paused, and returns the next value yielded by the generator function. If the generator exits without yielding another value, a StopIteration exception is raised. If the generator function does not catch the passed-in exception, or raises a different exception, then that exception propagates to the caller.

注意!!! 在上次yeild掛起的地方傳入exception!!!

代碼示例1-異常在生成器中被捕獲

  1. def gen(): 

  2. n=0 

  3. while True: 

  4. try: 

  5. yield n 

  6. n +=1 

  7. except OverflowError: 

  8. print("got it!") 

  9.  

  10. g = gen() 

  11.  

  12. result1 = next(g) 

  13. print(result1) 

  14.  

  15. result2 = g.throw(OverflowError) 

  16. print(result2) 

  17.  

  18. result3 = next(g) 

  19. print(result3) 

  20.  


pic-1582723999406.png

結果比較簡單,我們來看一下:

  • result1 = next(g) 沒毛病 n=0進去的,然和yield出來的也是0
  • result2=g.throw(OverflowError) 在上次掛起的地方,傳入這個異常. 然後異常被捕獲,n還是0 因爲下面的n+=n跟本沒執行.
  • 下面就沒啥好解釋的了

代碼示例2-異常在生成器中沒有被捕獲

  1. import sys 

  2. def gen(): 

  3. n = 0 

  4. while True: 

  5. yield n 

  6. n+=1 

  7.  

  8. g = gen() 

  9.  

  10. result1 = next(g) 

  11. print(result1) 

  12.  

  13. try: 

  14. result2 = g.throw(NameError) 

  15. except NameError: 

  16. print('Main function catch the exception') 

  17. print(sys.exc_info()) 

  18.  

  19. try: 

  20. print(result2) 

  21. except NameError: 

  22. print('cathe NameError') 

  23. print(sys.exc_info()) 

  24.  

  25. print(next(g)) 

  26.  

看着例子

  • throw的異常在生成器內部沒有捕獲的話,會直接傳遞到調用生成器的函數中去.
  • 這裏result2因爲賦值還沒有發生就拋出了異常,所以result2是不存在的.
  • 在異常拋出後,生成器終止,不能再繼續調用.


pic-1582723999406.png

代碼示例3

  1. def gen(): 

  2. try: 

  3. # 注意是在當前暫停的 yield 處拋出異常 

  4. # 所以要在這裏捕獲 

  5. yield 1 

  6. except Exception as e: 

  7. print('在生成器內部捕獲了異常') 

  8. print(e.args) 

  9. print('處理完畢,假裝什麼也沒發生') 

  10.  

  11. # yield 2 

  12.  

  13. g = gen() 

  14.  

  15. print(next(g)) 

  16.  

  17. result = g.throw(TypeError,'類型錯誤~~') 

  18.  

  19. print(result) 

  20.  

  • print(next(g)) 這裏沒問題,正常返回1
  • 下一步result = g.throw(TypeError,'類型錯誤~~')把異常整進去之後,記得!是從上一次yeild掛起的地方!!! 直接被生成器中的except捕獲了.
  • 之後因爲下面沒有yeild的值了,所以會報一個StopIteration的異常回來.


pic-1582723999406.png

close()方法

文檔定義

generator.close()
Raises a GeneratorExit at the point where the generator function was paused. If the generator function then exits gracefully, is already closed, or raises GeneratorExit (by not catching the exception), close returns to its caller. If the generator yields a value, a RuntimeError is raised. If the generator raises any other exception, it is propagated to the caller. close() does nothing if the generator has already exited due to an exception or normal exit.

文檔解讀:

  • 在生成器暫停的地方會返回GeneratorExit

代碼示例1-不在生成器內捕獲GeneratorExit異常

  1. def gen(): 

  2. print("1 will be yielded") 

  3. yield 1 

  4. print('2 will be yielded') 

  5. yield 2 

  6.  

  7. g = gen() 

  8.  

  9. print(next(g)) 

  10. g.close() 

  11. print(next(g)) 

  12.  


pic-1582723999406.png

  • close方法正常返回,且生成器內的GeneratorExit不傳遞給調用方
  • close之後生成器註銷,所以在此next的時候會報StopIteration異常

代碼示例2

  1. def gen(): 

  2. print("1 will be yielded") 

  3. try: 

  4. yield 1 

  5. except GeneratorExit: 

  6. print('get generator exit exception') 

  7. print("generator finished") 

  8.  

  9. g = gen() 

  10.  

  11. print(next(g)) 

  12. g.close() 

  13.  

生成器退出的時候拋出StopIteration異常,但是這個異常不會傳遞給調用方.

代碼示例3--GeneratorExit之後還有yield語句

  1. def gen(): 

  2. try: 

  3. yield 1 

  4. except GeneratorExit: 

  5. print('捕獲到 GeneratorExit') 

  6. print('嘗試在 GeneratorExit 產生後 yield 一個值') 

  7. yield 2 

  8.  

  9. print('生成器結束') 

  10.  

  11. g = gen() 

  12. next(g) 

  13. g.close() 

  14.  


pic-1582723999407.png

  • 在GeneratorExit拋出之後還有yield語句的話 有可能會產生RuntimeError.
  • 所以不要在except和finally中寫yield,因爲不知道啥時候就會出問題.
  • 另外,在生成器被垃圾回收的時候,也會自動調用close方法

最後,對於一個已經關閉的生成器對象,close方法不會有任何的作用. 不執行任何操作.
換句話說,只有激活的生成器纔會觸發GeneratorExit異常.

終於特麼寫完了...累死我了...

本文參考了 CSDN(糰子大元帥)的文章

Python 生成器與它的 send,throw,close 方法

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章