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