測開之數據類型· 第4篇《迭代器、生成器》

堅持原創輸出,點擊藍字關注我吧

做者:清菡
博客:oschina、雲+社區、知乎等各大平臺都有。微信

因爲微信公衆號推送改成了信息流的形式,防止走丟,請給加個星標 ⭐,你就能夠第一時間接收到本公衆號的推送!

文章總覽圖

目錄

  • 1、迭代器函數

    • 1.迭代協議
    • 2.什麼是迭代器呢?
    • 3.可迭代對象
    • 4.這個是可迭代對象和迭代器的區別
  • 2、生成器性能

    • 1.什麼是迭代操做?
    • 2.生成器和迭代器有什麼不一樣呢?
    • 3.生成器比迭代器多了 3 種方法
    • 4.爲何生成器有的方法,迭代器沒有?
    • 5.數據發送到生成器,在哪一個地方呢?
  • 3、系列推薦

1、迭代器

1.迭代協議

一種是包含iter方法的,另外一種是包含getitem方法的(好比str對象就沒有iter方法,可是同樣可以迭代),只要對象中包含了這兩種方法的任意一種,那麼這個對象就能夠進行迭代操做,也就是實現了迭代協議。測試

2.什麼是迭代器呢?

生成器是迭代器的一種。迭代器的範圍比生成器更廣。只要能夠經過next(),從裏面一個一個往外面取值,都被稱爲迭代器。編碼

關於要建立一個迭代器對象,那麼內部要實現一個迭代器的協議。spa

2.1 迭代器協議

  • 實現了迭代器協議的對象(實現方式:對象內部定義了一個iter()方法)。
  • 對象實現了__next__方法。
  • __next__方法返回了某個數值(固然通常狀況下,咱們須要的是返回這個對象的特定的數字,而且按照必定的順序進行依次返回)。
  • __next__方法須要在值取完的時候,拋出stopiteration的錯誤信息。

3.可迭代對象

有個東西須要區分,一個是迭代器,一個是可迭代對象。code

只要內部實現了迭代協議的就是一個可迭代對象(可迭代對象能夠進行相關的迭代操做,好比for循環,map函數等)。對象

能夠用 for 循環進行遍歷的,那麼都是可迭代對象。可迭代對象不必定是迭代器,迭代器是在可迭代基礎上,它內部要首先定義一個__next_方法。blog

迭代器內部實現了一個__next_方法,實現了這個方法以後,經過__next_這個函數才能夠對這個迭代器進行一個取值。rem

還有個iter()方法,這個方法可將可迭代對象轉換成一個迭代器。

yieldreturn是 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.這個是可迭代對象和迭代器的區別

2、生成器

生成器是迭代器的一種。

迭代器是在可迭代對象的基礎上實現了__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()不發進去內容,返回出來是個空的。

舒適提示:生成器<迭代器<可迭代對象

3、系列推薦


公衆號 「清菡軟件測試」 首發,更多原創文章:清菡軟件測試 109+原創文章,歡迎關注、交流,禁止第三方擅自轉載。

相關文章
相關標籤/搜索