測開之資料型別· 第4篇《迭代器、生成器》
阿新 • • 發佈:2020-12-19
## 堅持原創輸出,點選藍字關注我吧
![](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+原創文章**,歡迎關注、交流,禁止第三方擅自