自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

重新認(rèn)識(shí)生成器Generator

開發(fā) 前端
我們知道,函數(shù)體包含 yield 關(guān)鍵字的函數(shù)不是一個(gè)普通函數(shù)。這種函數(shù)叫做 生成器 ( generator ),一般用于循環(huán)處理結(jié)構(gòu),應(yīng)用得當(dāng)可以極大優(yōu)化內(nèi)存使用效率。

 [[395138]]

本文轉(zhuǎn)載自微信公眾號(hào)「小菜學(xué)編程」,作者fasionchan。轉(zhuǎn)載本文請(qǐng)聯(lián)系小菜學(xué)編程公眾號(hào)。

基本用法

我們知道,函數(shù)體包含 yield 關(guān)鍵字的函數(shù)不是一個(gè)普通函數(shù)。這種函數(shù)叫做 生成器 ( generator ),一般用于循環(huán)處理結(jié)構(gòu),應(yīng)用得當(dāng)可以極大優(yōu)化內(nèi)存使用效率。例如,設(shè)計(jì)一個(gè)函數(shù),打開文件并將每一行轉(zhuǎn)成大寫并返回:

  1. def read_file_upper(path): 
  2.     lines = [] 
  3.     with open(path) as f: 
  4.         for line in f: 
  5.             lines.append(line.upper()) 
  6.     return lines 

這個(gè)版本的函數(shù),在內(nèi)部創(chuàng)建了一個(gè) list 對(duì)象,用于保存轉(zhuǎn)換結(jié)果。for 循環(huán)則遍歷文件每一行,將其轉(zhuǎn)成大寫并追加到列表中。這樣一來,文件中的每一行均需要保存在列表中,如果文件很大,內(nèi)存開銷可想而知。

我們可以借助 yield 關(guān)鍵字,將 read_file_upper 函數(shù)改成生成器版本。函數(shù)主體邏輯沒有任何變化,只是將每行數(shù)據(jù)的處理結(jié)果通過 yield 逐個(gè)返回,而不是收集到 list 對(duì)象后再返還。

  1. def iter_file_upper(path): 
  2.     with open(path) as f: 
  3.         for line in f: 
  4.             yield line.upper() 

如果現(xiàn)在有一個(gè)文本文件 data.txt ,里面包含以下內(nèi)容:

  1. hello, world 
  2. life is short, use python 
  3. my wechat id is: coding-fan 
  4. bye 

用 iter_file_upper 生成器,我們可以這樣對(duì)它進(jìn)行處理:

  1. >>> for line in iter_file_upper('text.txt'): 
  2. ...     print(line.strip()) 
  3. HELLO, WORLD 
  4. LIFE IS SHORT, USE PYTHON 
  5. MY WECHAT ID IS: CODING-FAN 
  6. BYE 

iter_file_upper 生成器用法與 read_file_upper 函數(shù)大致相同,但它不會(huì)一次性拿住文件所有數(shù)據(jù)行,而是逐行處理、逐個(gè)返回,這樣便將內(nèi)存使用量降到最低。

行為觀察

那么,生成器為什么會(huì)有這樣的奇效呢?我們接著觀察:

  1. >>> g = iter_file_upper('text.txt'
  2. >>> g 
  3. <generator object iter_file_upper at 0x103becd68> 

我們調(diào)用 iter_file_upper 后,得到一個(gè)生成器對(duì)象,而不是文件處理結(jié)果,這時(shí) iter_file_upper 還未開始執(zhí)行。

當(dāng)我們調(diào)用 next 函數(shù)從生成器接收下一個(gè)數(shù)據(jù)時(shí),iter_file_upper 開始執(zhí)行并在 yield 處停下來,并把第一行的處理結(jié)果返回給我們:

  1. >>> next(g) 
  2. 'HELLO, WORLD\n' 

這時(shí),生成器處于暫停狀態(tài),沒有我們的指令,它不會(huì)接著處理第二行數(shù)據(jù)。

當(dāng)我們?cè)俅螆?zhí)行 next 函數(shù)時(shí),生成器再次恢復(fù)執(zhí)行,處理下一行數(shù)據(jù)并在 yield 處再次暫停:

  1. >>> next(g) 
  2. 'LIFE IS SHORT, USE PYTHON\n' 

生成器記住了自己的執(zhí)行進(jìn)度,每次調(diào)用 next 函數(shù),它總是處理并生產(chǎn)下一個(gè)數(shù)據(jù),完全不用我們瞎操心:

  1. >>> next(g) 
  2. 'MY WECHAT ID IS: CODING-FAN\n' 
  3. >>> next(g) 
  4. 'BYE\n' 

當(dāng) iter_file_upper 代碼邏輯執(zhí)行完畢,它將給 next 拋一個(gè)異常,以此通知調(diào)用者它已經(jīng)結(jié)束了:

  1. >>> next(g) 
  2. Traceback (most recent call last): 
  3.   File "<stdin>", line 1, in <module> 
  4. StopIteration 

因此,我們可以簡(jiǎn)單認(rèn)為 for-in 循環(huán)在 Python 虛擬機(jī)內(nèi)部是這樣實(shí)現(xiàn)的:

  • 不斷調(diào)用 next 函數(shù)讓生成器產(chǎn)出數(shù)據(jù);
  • 直到生成器拋出 StopIteration 異常;

在經(jīng)典的線程模型中,每個(gè)線程有一個(gè)獨(dú)立的執(zhí)行流,只能執(zhí)行一個(gè)任務(wù)。如果一個(gè)程序需要同時(shí)處理多個(gè)任務(wù),可以借助 多進(jìn)程 或者 多線程 技術(shù)。假設(shè)一個(gè)站點(diǎn)需要同時(shí)服務(wù)多個(gè)客戶端連接,可以為每個(gè)連接創(chuàng)建一個(gè)獨(dú)立的線程進(jìn)行處理。

不管線程還是進(jìn)程,切換時(shí)都會(huì)帶來巨大的開銷:用戶態(tài)/內(nèi)核態(tài)切換、執(zhí)行上下文保存和恢復(fù)、CPU緩存刷新等等。因此,用線程或進(jìn)程來驅(qū)動(dòng)小任務(wù)的執(zhí)行,顯然不是一個(gè)理想的選擇。

那么,除了線程和進(jìn)程,還有其他解決方案嗎?

 

責(zé)任編輯:武曉燕 來源: 小菜學(xué)編程
相關(guān)推薦

2022-03-03 08:30:41

GeneratorES6函數(shù)

2014-01-06 11:23:54

Mesos設(shè)計(jì)架構(gòu)

2016-11-07 11:34:28

數(shù)據(jù)可視化大數(shù)據(jù)

2016-12-13 15:41:40

JavaHashMap

2019-02-24 21:27:26

物聯(lián)網(wǎng)網(wǎng)關(guān)物聯(lián)網(wǎng)IOT

2019-10-31 13:40:52

JavaPHP編程語言

2019-09-02 08:53:46

程序員

2021-11-11 05:00:02

JavaMmap內(nèi)存

2020-09-17 07:08:04

TypescriptVue3前端

2017-01-03 17:22:16

公共云安全

2022-10-27 13:58:32

Python編程生成器

2009-08-28 10:40:18

開源Linux防火墻生成器Linux操作系統(tǒng)

2015-03-19 10:15:54

程序員價(jià)值程序員價(jià)值

2010-10-22 11:10:24

軟考

2019-01-18 13:32:16

2019-04-15 14:32:11

2012-01-11 09:12:25

程序員

2022-10-09 11:46:55

機(jī)器人人工智能

2009-11-26 16:57:09

Cisco路由器ARP

2010-02-25 09:57:35

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)