1. 程式人生 > >測開之資料型別· 第4篇《迭代器、生成器》

測開之資料型別· 第4篇《迭代器、生成器》

## 堅持原創輸出,點選藍字關注我吧 ![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201218144235.png) 作者:清菡 部落格:oschina、雲+社群、知乎等各大平臺都有。 > 由於微信公眾號推送改為了資訊流的形式,防止走丟,請給加個星標 ⭐,你就可以第一時間接收到本公眾號的推送! # 文章總覽圖 ![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201218164309.png) # 目錄 - 一、迭代器 - 1.迭代協議 - 2.什麼是迭代器呢? - 3.可迭代物件 - 4.這個是可迭代物件和迭代器的區別 - 二、生成器 - 1.什麼是迭代操作? - 2.生成器和迭代器有什麼不同呢? - 3.生成器比迭代器多了 3 種方法 - 4.為什麼生成器有的方法,迭代器沒有? - 5.資料傳送到生成器,在哪個地方呢? - 三、系列推薦 ## 一、迭代器 ### 1.迭代協議 一種是包含`iter`方法的,另一種是包含`getitem`方法的(比如`str`物件就沒有`iter`方法,但是一樣能夠迭代),只要物件中包含了這兩種方法的任意一種,那麼這個物件就可以進行迭代操作,也就是實現了迭代協議。 ### 2.什麼是迭代器呢? 生成器是迭代器的一種。迭代器的範圍比生成器更廣。只要可以通過`next()`,從裡面一個一個往外面取值,都被稱為迭代器。 關於要建立一個迭代器物件,那麼內部要實現一個迭代器的協議。 #### 2.1 迭代器協議 - 實現了迭代器協議的物件(實現方式:物件內部定義了一個`iter()`方法)。 - 物件實現了`__next__`方法。 - `__next__`方法返回了某個數值(當然一般情況下,我們需要的是返回這個物件的特定的數字,並且按照一定的順序進行依次返回)。 - `__next__`方法需要在值取完的時候,丟擲`stopiteration`的錯誤資訊。 ### 3.可迭代物件 **有個東西需要區分,一個是迭代器,一個是可迭代物件。** 只要內部實現了迭代協議的就是一個可迭代物件(可迭代物件可以進行相關的迭代操作,比如`for`迴圈,`map`函式等)。 可以用 for 迴圈進行遍歷的,那麼都是可迭代物件。可迭代物件不一定是迭代器,迭代器是在可迭代基礎上,它內部要首先定義一個`__next_`方法。 迭代器內部實現了一個`__next_`方法,實現了這個方法之後,通過`__next_`這個函式才可以對這個迭代器進行一個取值。 還有個`iter()`方法,這個方法可將可迭代物件轉換成一個迭代器。 `yield`和`return`是 2 個東西。`yield`只是暫停那個生成器函式。`yield`可以從生成器裡面生成一個內容。 **列表可以進行 for 迴圈,可以進行 for 迴圈遍歷,它就是個可迭代物件。** 列表是可以通過 for 迴圈遍歷的,但是它不是迭代器。 **迭代器是可以通過`next()`進行取值的。** **生成器也是迭代器,生成器是可以通過`next()`去取值。那麼,生成器它是迭代器的一種,是屬於迭代器的。** 你看,報錯了: ```PYTHON # 列表 # 可迭代物件:可以for迴圈遍歷的都是可迭代物件 li = [1,2,3,4] next(li) print(next(li)) ``` ![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201217170039.png) 提示:列表它不是一個迭代器。 不是個迭代器,不能通過這個去取值。**要把一個可迭代物件轉換成一個迭代器的話,通過`iter()`這個函式把可迭代物件放進去,它能夠返回一個迭代器。** 你看,這樣就能獲取到了: ```PYTHON # 列表 # 可迭代物件:可以for迴圈遍歷的都是可迭代物件 li = [1,2,3,4] li1 = iter(li) print(next(li1)) print(next(li1)) ``` 通過`iter()`這個函式,來處理某個物件,它實際上相當於觸發這個物件內部的一個`__iter__`這個方法。 **咱們看看`list()`的原始碼:** ![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201217172351.png) ![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201217172641.png) 通過`iter()`這個函式把物件`li`傳進去的時候,它會觸發`li`這個物件對應的`__iter_`這個方法。 如果通過`next()`去取值,把`li1`這個物件傳進去的時候,實際上是觸發這個物件的`__next__`方法。 它的類裡面只有這個`__iter__`方法。 迭代器可以通過`__next__`取值。迭代器內部實現了`__next__`方法。 **迭代器內部實現了 `__iter__`方法之外,還實現了`__next__` 方法。** ### 4.這個是可迭代物件和迭代器的區別 ![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201217175649.png) ## 二、生成器 **生成器是迭代器的一種。** **迭代器是在可迭代物件的基礎上實現了`__iter_`方法。迭代器和生成器都可以支援迭代操作。** ### 1.什麼是迭代操作? for 迴圈。 ### 2.生成器和迭代器有什麼不同呢? **生成器是迭代器的一種。** 剛才用起來的時候好像沒有什麼區別,列印下這個型別看看。 **可以看到,它返回的是個列表迭代器物件:** ![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201217180611.png) **這個是生成器物件:** ![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201217181940.png) `li1 = iter(li)`這個是可迭代物件。然後通過`iter()`轉換成一個迭代器。 ### 3.生成器比迭代器多了 3 種方法 | send()方法 | 傳送資料 | | ---------- | ------------------------- | | close() 方法 | 關閉生成器 | | throw() 方法 | 使用的 throw 指令丟擲錯誤 | **生成器是有`send`這個方法的,迭代器是沒有的。** 例如,前面有個生成器叫做`tu`: ```PYTHON # () 生成器表示式 tu = (i for i in range(1000))#生成器物件 print(tu) ``` `tu`可以呼叫`send()`這個方法,可以與生成器進行互動,可將資料傳輸到生成器裡面。 ### 4.為什麼生成器有的方法,迭代器沒有? 舉個栗子: **生成器是迭代器的一種。** 例如定義了一個父類,再有個子類,父類創建出一個物件,子類創建出一個物件。子類有自己的方法。**父類建立的出來的物件裡面,肯定沒有子類物件裡面的方法。** 子類裡面有的方法,父類裡面沒有。 迭代器就是“父類”。生成器就是“子類”。 ```PYTHON def gen(): for i in range(1,5): yield i gen() ``` 生成器執行的時候,呼叫函式`gen()`,呼叫這個函式的時候,這個函式裡面的程式碼不會直接執行。 ![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201217185852.png) 程式碼修改成這樣: ```PYTHON def gen(): for i in range(1,5): yield i g = gen() print(g) ``` ![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201217190216.png) 只有通過`next()`方法往生成器裡面取值的時候,它才會從程式碼上面往下面執行。 這個`send()`方法可將資料傳到生成器裡面。使用`next()`,從生成器裡面獲取出一個值。如果使用`send()`方法,它也能夠獲取出來一條資料。 ```PYTHON def gen(): for i in range(1,5): se = yield i print(se) g = gen() print(next(g)) print(g.send(100)) ``` `send()`方法可以往生成器裡面傳入一個值。 ![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201217191508.png) 通過`send()`方法生成資料的時候,它也可以往裡面傳送一個 100 的值。 ### 5.資料傳送到生成器,在哪個地方呢? ![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201217191854.png) 如果通過`next()`去取值的話,這個`yield`完畢後是沒有返回內容的。 ![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201217192311.png) ![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201217192834.png) **程式碼詳解:** **第一輪:** 迴圈進來,通過`next()`去取值生成了一個 1: ```PYTHON def gen(): for i in range(1,5): se = yield i print(se) g = gen() print(next(g)) ``` ![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201217193205.png) **第二輪:** 通過`print(g.send(100))`去傳送值,然後列印: ```PYTHON def gen(): for i in range(1,5): se = yield i print('se的值:',se) g = gen() print(next(g)) print(g.send(100)) ``` ![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201217194010.png) 在第一輪結束之後,在`yield`這裡,`yield`完畢就停止了。在第一輪`yield`完之後,第二輪通過`send()`傳值進去,傳到`se`那裡,打印出來 100。 然後再往上返回一個數據,又暫停,返回第二條資料就是個 2。 **第三輪:** 通過`next()`再去生成一條元素,又觸發了`yield i`這個地方,這裡釋放了,往後面走,往後面走的話,但是沒有放資料進來,這個時候`se`是空的,打印出來的`se`是空的。 然後再往上,生成一條元素到 3,然後又停在`yield i`這個地方了,生成完元素,把這個值返回出去。 ```PYTHON def gen(): for i in range(1,5): se = yield i print('se的值:',se) g = gen() print(next(g)) print(g.send(100)) print(next(g)) ``` ![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201217194529.png) 再次`next()`或者`send()`來觸發它的時候,它會這樣走: ![](https://gitee.com/qinghanstudy/qinghan/raw/master/img/20201217195737.png) **注意:** `yield`接收不是存在`i`中,這個`yield`返回出來的`i`是遍歷出來的內容。 `send()`發進去的,是`yield i`這裡執行完畢之後,當下一個`send()`觸發的時候,它把這個值傳送到`yield i`這裡執行完畢之後的一個結果。 `yield i`這裡把這個`i`返回出去,就停在這裡不動了。`send()`傳送個數據進去,那麼資料就傳送到個`yield i`這地方。 相當於`yield i`這個地方返回的一個結果,也就是`send()`發進去的內容,如果`send()`不發進去內容,返回出來是個空的。 **溫馨提示:**`生成器<迭代器<可迭代物件` ## 三、系列推薦 - [測開入門篇《環境管理、編碼規範、專案結構》](https://mp.weixin.qq.com/s/aXKYIZ8K4f7dWOfCQCDBXg) - [資料型別· 第 1 篇《元組和列表的效能分析、命名元組》](https://mp.weixin.qq.com/s/6bKgDRYONbM9V8vUxCH0ug) - [資料型別第 2 篇「字典和集合的原理和應用」](https://mp.weixin.qq.com/s/baCQFwWwR8KbXkFYBsLaJA) - [測開之資料型別· 第 3 篇《列表推導式、字典推導式、2 種方式建立生成器》](https://mp.weixin.qq.com/s/McgUcQxjujBILR9QtzJ95w) - [《Web 自動化》基礎知識腦圖](https://mp.weixin.qq.com/s/yCifHeeN6DbXNXazr_pFRQ) --- 公眾號 **「清菡軟體測試」** 首發,更多原創文章:**清菡軟體測試 109+原創文章**,歡迎關注、交流,禁止第三方擅自