天天看點

python3中的疊代器與生成器

這個東西輸出可以腦補一下, 結果是[20,21,22,23], 而不是[10, 11, 12, 13]。

要說生成器,必須首先說疊代器

區分iterable,iterator與itertion

講到疊代器,就需要差別幾個概念:iterable,iterator,itertion, 看着都差不多,其實不然。下面區分一下。

itertion: 就是疊代,一個接一個(one after another),是一個通用的概念,比如一個循環周遊某個數組。

iterable: 這個是可疊代對象,屬于python的名詞,範圍也很廣,可重複疊代,滿足如下其中之一的都是iterable:

可以for循環: for i in iterable

可以按index索引的對象,也就是定義了getitem方法,比如list,str;

定義了iter方法。可以随意傳回。

可以調用iter(obj)的對象,并且傳回一個iterator

iterator: 疊代器對象,也屬于python的名詞,隻能疊代一次。需要滿足如下的疊代器協定

定義了iter方法,但是必須傳回自身

定義了next方法,在python3.x是next。用來傳回下一個值,并且當沒有資料了,抛出StopIteration可以保持目前的狀态

首先str和list是iterable 但不是iterator:

從next函數中隻能向前取資料,一次取一個可以看出來,不過不能重複取資料,那這個可不可以解決呢?

我們知道iterator隻能疊代一次,但是iterable對象則沒有這個限制,是以我們可以把iterator從資料中分離出來,分别定義一個iterable與iterator如下:

Python支援在容器上疊代,通過兩個方法實作,允許使用者自定義,序列總是支援疊代方法.

疊代器的功能可以使用清單代替,但如果有很多值,清單就會占用太多的記憶體,而如果有可以一個接一個地計算值的函數,那麼就可以在使用時采用計算一個值時擷取一個值,占用更少記憶體。

當周遊一個疊代器的時候,它會修改内部狀态,導緻你隻能向前擷取下一個元素,不能通過疊代器通路後面一個元素;也就是說當你通過疊代器通路了一個元素以後,在目前循環中不能後退繼續通路該元素了,除非你重新生産疊代器對象進行周遊。

輸出結果如下:

疊代器對象被要求支援下面的兩個方法,合起來形成疊代協定。

iterator._iter_()

傳回疊代器對象自身。為了允許容器和疊代器被用于for和in語句中,必須實作該方法。

iterator._next_()

傳回容器的下一個條目。如果沒有更多的條目,抛出StopIteration異常

另外需要注意的是在疊代器中next方法是return下一個元素的值,不像下面介紹的生成器yield一個元素

任何使用yield的函數都稱之為生成器.

首先需要明确的就是生成器也是iterator疊代器,因為它遵循了疊代器協定.生成器函數跟普通函數隻有一點不一樣,就是把 return 換成yield,其中yield是一個文法糖,内部實作了疊代器協定,同時保持狀态可以挂起。

(如果換成return 函數就傳回了)

另外一種說法:生成器就是一個傳回疊代器的函數,與普通函數的差別是生成器包含yield語句,更簡單點了解生成器就是一個疊代器。

使用yield,可以讓函數生成一個序列,該函數傳回的對象類型是”generator”,通過該對象連續調用next()方法傳回序列值。

生成器函數隻有在調用_next()_方法的時候才開始執行函數裡面的語句.

在調用count函數時:c=count(7),并不會列印”counting”隻有等到調用c.next()時才真正執行裡面的語句。每次調用next()方法時,count函數會運作到語句yield n處為止,next()的傳回值就是生成值n,此處為了檢視傳回值列印了出來,可以去掉,再次調用next()方法時,函數繼續執行yield之後的語句(熟悉Java的朋友肯定知道Thread.yield()方法,作用是暫停目前線程的運作,讓其他線程執行),如:

yield作用就是傳回一個生成器,它會儲存目前函數狀态,記錄下一次函數被調用next的時候運作狀态

如果一直調用next方法,當執行到沒有可疊代的值後,程式就會報錯:

<code>Traceback (most recent call last): File "", line 1, in StopIteration</code>

是以一般不會手動的調用next方法,而使用for循環

可以調試看輸出,就明白了。

另外還有一種定義生成器的方法:生成器表達式”()”