【python】生成器
生成器
直接總結
創建與使用生成器
直接使用生成器表達式就可以快速創建一個生成器
gen = (i for i in range(10)) print(type(gen)) for i in gen:print(i, end=" ")# <class 'generator'> # 0 1 2 3 4 5 6 7 8 9生成器實現了__next__可以使用next()來獲取下一個值,當然也可以使用for循環遍歷
更加常見的創建生成器的方式是使用yield關鍵字,一個函數如果出現yield關鍵字這個函數就會變成生成器,當函數運行到yield時會暫停下來,”返回“一個結果,下一次喚醒生成器時,函數會從停下來的地方繼續運行
def builder_demo():yield 0yield 1return 3if __name__ == '__main__':bd = builder_demo()print(type(bd))print(next(bd))print(next(bd))print(next(bd))print(next(bd)) <class 'generator'> 0 1 Traceback (most recent call last):File "E:/python/coroutine_test.py", line 12, in <module>print(next(bd)) StopIteration: 3當沒有下一個元素時調用next會拋出StopIteration異常,return的值會作為異常的值
if __name__ == '__main__':bd = builder_demo()while True:try:print(next(bd))except StopIteration as e:print(f'result is {e.value}')break 0 1 result is 3yield不但可以“傳遞出值”,也可以接收值
def builder_demo():news = yield 0print(f'news: {news}')news1 = yield 1print(f'new1: {news1}')return 3if __name__ == '__main__':bd = builder_demo()print(next(bd))result1 = bd.send("hello")print(result1)result2 = bd.send("hello2")print(result2) 0 news: hello 1 new1: hello2 Traceback (most recent call last):File "E:python/coroutine_test.py", line 14, in <module>result2 = bd.send("hello2") StopIteration: 3往暫停處傳遞消息使用生成器的send()方法,這個方法還可以自動迭代到生成器中的下一個對象(有next())的作用。
生成器是先yield出數據,等到下一次生成器被喚醒時,才會接收send()的數據,然后再yield出下一個數據,所以不能一開始就直接調用send()發送具體的值,會拋出TypeError
TypeError: can't send non-None value to a just-started generator應該先執行一次next()或執行一次generator.send(None),讓生成器yield出數據,send(None)的作用與next()基本一樣
生成器也可以停止,使用close()方法
def builder_demo():news = yield 0print(f'news: {news}')news1 = yield 1print(f'new1: {news1}')yield 4yield 5return 3if __name__ == '__main__':bd = builder_demo()# print(next(bd))print(bd.send(None))result1 = bd.send("hello")print(result1)result2 = bd.send("hello2")print(result2)bd.close()print(next(bd)) 0 news: hello 1 new1: hello2 4 Traceback (most recent call last):File "E:python/coroutine_test.py", line 20, in <module>print(next(bd)) StopIteration在close()之后再使用next(),會拋出StopIteration異常
除此之外,還可以向生成器發送異常
if __name__ == '__main__':bd = builder_demo()print(bd.send(None))bd.throw(Exception, TypeError("throw new error"))print(next(bd)) 0 Traceback (most recent call last):File "E:python/coroutine_test.py", line 19, in <module>bd.throw(Exception, TypeError("throw new error"))File "E:python/coroutine_test.py", line 2, in builder_demonews = yield 0 TypeError: throw new erroryield from
yield from 是python3.3 PEP380 新添加的特性,它允許將一個生成器的部分操作委派給另一個生成器,除了向子生成器委派任務,yield from也可以直接作用于迭代器,將迭代器中的每個對象逐一yield出來,如:
def demo(*args, **kwargs):for i in args:for j in i:yield j # 等價于 def demo(*args, **kwargs):for i in args:yield from i上面的函數其實就是itertools.chain() 作用是將多個迭代器中的元素迭代出來
生成器嵌套
1、調用方:調用委派生成器的客戶端(調用方)代碼
2、委托生成器:包含yield from表達式的生成器函數
3、子生成器:yield from后面加的生成器函數
yield from 可以架設一個調用方到子生成器之間的雙向橋梁
final_result = {}def calculate():total = 0nums = []while True:info = yieldif not info:breaktotal += infonums.append(info)# return 的值會被賦值給yield from 左邊的變量return total, numsdef middle(key: str, gen):while True:final_result[key] = yield from gen()print(final_result)def main():data = {"apple": [230, 569, 234, 776],"banana": [564, 213, 798, 327],"strawberry": [98, 76, 120, 436, 55],"orange": [78, 67, 345, 124]}for key, value in data.items():# 不要傳遞calculate()!mid = middle(key, calculate)mid.send(None) # 初激for v in value:mid.send(v)mid.send(None)if __name__ == '__main__':main()為什么“多此一舉”架設一個“橋梁”?
- yield from 在內部處理了大量可能的異常,簡化開發,提高代碼安全性和可讀性
生成器的作用
總結
以上是生活随笔為你收集整理的【python】生成器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 8数据提供什么掩膜产品_博硕能为你提供什
- 下一篇: Ubuntu操作系统