生成器generator
生成器可以簡單有效的創建龐大的可迭代對象,而不需要在直接在內存中創建存儲整個序列
可以使用生成器推導式或者生成器函數來創建生成器
生成器函數返回數據時使用yield語句,而不是使用return
>>> def countdown(n):
... print("Counting down from %d" %n)
... while n>0:
... yield n
... n -=1
... return 'done'
...
>>> c = countdown(10) #c爲一個生成器對象
可以通過next()函數或者生成器對象的__next__()方法來獲得生成器的下一個返回值
當計算到最後一個元素後,沒有更多的元素時,拋出StopInteration,生成器終止
>>> c = countdown(3)
>>> next(c)
Counting down from 3
3
>>> c.__next__()
2
>>> next(c)
1
>>> next(c)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration: done
通常直接通過for循環來迭代生成器,並且不需要關心StopInteration
但是用for循環調用生成器時,發現拿不到生成器的return語句的返回值。
如果想要拿到返回值,必須捕獲StopInteration錯誤,返回值包含在StopInteration的value中
>>> while True:
... try:
... x = next(c)
... print(c)
... except StopIteration as e:
... print('Generator return value:', e.value)
... break
...
當一個生成器沒有全部迭代完成,但是不在使用了,可以調用close()方法關閉生成器,通常不必手動調用close()方法
c = countdown(10)
>>> next(c)
1
>>> c.close()
>>> next(c)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
協程
協程可以理解爲就像生成器那樣可以暫停的函數在函數內,當yield語句作爲表達式使用,出現在賦值運算符的右邊,在向函數發送值時函數將執行
這種生成器函數可以被稱爲協程函數,而對協程函數的調用可以獲得協程對象
"協程"一詞可以用於協程函數和協程對象
>>> def coroutine():
... print("Start Coroutine")
... r = 'ok'
... while True:
... n = yield r
... print('receiver %s' % n)
...
>>> c = coroutine()
>>> c.send(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can't send non-None value to a just-started generator
>>> c.send(None) #next(c)
Start Coroutine
'ok'
>>> c.send(1)
receiver 1
'ok'
>>> c.close()
>>> c.send(2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
在發送數據之前,應該調用c.send(None)啓動生成器或者使用next()函數,使協程執行第一個yield之前的語句
啓動後協程會掛起,等待協程對象c的send()方法發送值
yield語句會接收send()發送的值,並返回yield 語句後的值
協程一般會不斷執行下去,可以通過close()方法關閉協程
我們可以使用asyncio模塊中的@asyncio.coroutine裝飾器來使協程函數在調用時自動運行到yield語句前,而不需要提前調用send(None)或next()函數
具體的asyncio模塊和協程的講解可以看另一篇文章