詳解Python3中的迭代器和生成器及其區(qū)別
介紹
本篇將介紹Python3中的迭代器與生成器,描述可迭代與迭代器關(guān)系,并實(shí)現(xiàn)自定義類的迭代器模式。
迭代的概念
上一次輸出的結(jié)果為下一次輸入的初始值,重復(fù)的過程稱為迭代,每次重復(fù)即一次迭代,并且每次迭代的結(jié)果是下一次迭代的初始值
注:循環(huán)不是迭代
while True: #只滿足重復(fù),因而不是迭代
print('====>')
迭代器
1.為什么要有迭代器?
對(duì)于沒有索引的數(shù)據(jù)類型,必須提供一種不依賴索引的迭代方式。
2.迭代器定義:
迭代器:可迭代對(duì)象執(zhí)行__iter__方法,得到的結(jié)果就是迭代器,迭代器對(duì)象有__next__方法
它是一個(gè)帶狀態(tài)的對(duì)象,他能在你調(diào)用next()方法的時(shí)候返回容器中的下一個(gè)值,任何實(shí)現(xiàn)了__iter__和__next__()方法的對(duì)象都是迭代器,__iter__返回迭代器自身,__next__返回容器中的下一個(gè)值,如果容器中沒有更多元素了,則拋出StopIteration異常
可迭代的(iterable)
Python標(biāo)準(zhǔn)庫中存在著一些可迭代對(duì)象,例如:list, tuple, dict, set, str等。
可以對(duì)這些迭代對(duì)象,進(jìn)行for-in等迭代操作,例如:
for s in "helloworld": print(s)
編譯器若想迭代一個(gè)對(duì)象a,則會(huì)自動(dòng)調(diào)用iter(a)獲取該對(duì)象的迭代器(iterator),如果iter(a)拋出異常,則對(duì)象a不可迭代。
判斷對(duì)象是否可迭代
原生函數(shù)iter(instance) 可以判斷某個(gè)對(duì)象是否可迭代,它的工作流程大概分為以下3個(gè)步驟:
- 檢查對(duì)象instance是否實(shí)現(xiàn)了__iter__方法,并調(diào)用它獲取返回的迭代器(iterator)。
- 如果對(duì)象沒有實(shí)現(xiàn)__iter__方法,但是實(shí)現(xiàn)了__getitem__方法,Python會(huì)生成一個(gè)迭代器。
- 如果上述都失敗,則編譯器則拋出TypeError錯(cuò)誤,‘xxx' Object is not iterable。
自定義類實(shí)現(xiàn)__iter__方法
根據(jù)第一條,我們自定義類Iter1實(shí)現(xiàn)__iter__方法使該類的對(duì)象可迭代。
class Iter1:
def __init__(self, text):
self.text = text
def __iter__(self):
return iter(self.text)
iter1 = Iter1("hello")
for s in iter1:
print(s)
Iter1類實(shí)現(xiàn)了__iter__方法,通過iter()調(diào)用,得到可迭代對(duì)象text的迭代器并返回,實(shí)現(xiàn)了迭代器協(xié)議,因此可以通過for-in等方式對(duì)該對(duì)象進(jìn)行迭代。
第二條通常都是針對(duì)Python中的序列(sequence)而定義,例如list,為了實(shí)現(xiàn)sequence協(xié)議,需要實(shí)現(xiàn)__getitem__方法。
class Iter2:
def __init__(self, sequence):
self.sequence = sequence
def __getitem__(self, item):
return self.sequence[item]
iter2 = Iter2([1, 2, 3, 4])
for s in iter2:
print(s)
實(shí)際上,為了避免版本后序改動(dòng),Python標(biāo)準(zhǔn)庫中的序列除了實(shí)現(xiàn)了__getitem__方法,也實(shí)現(xiàn)了__iter__方法,因此我們在定義序列時(shí)也應(yīng)實(shí)現(xiàn)__iter__。
綜上,如果顯示判斷某個(gè)對(duì)象是否可迭代,應(yīng)該調(diào)用iter(instance)是否拋出異常,因?yàn)橹粚?shí)現(xiàn)了__getitem__的序列也是可迭代的(例子中Iter2的對(duì)象是可迭代的,但isinstance(iter2, abc.Iterator)返回結(jié)果是False)。同時(shí),如果在調(diào)用iter后進(jìn)行迭代操作不必顯示判斷,可以用try/except方式包裝代碼塊。
iterable vs iterator(可迭代vs迭代器)
iterable定義
任何可以由原生函數(shù)iter獲取到迭代器的對(duì)象
任何實(shí)現(xiàn)了__iter__方法并返回迭代器的對(duì)象
所有的序列(實(shí)現(xiàn)了__getitem__)
Python通過獲取到可迭代對(duì)象的迭代器(iterator)實(shí)現(xiàn)迭代,例如for-in的實(shí)現(xiàn)其實(shí)是在內(nèi)部獲取到了迭代器進(jìn)行操作。for-in機(jī)制可以理解為下述代碼:
s = 'hello'
it = iter(s)
while (True):
try:
print(next(it))
except StopIteration:
del it
break
StopIteration異常將在迭代器耗盡后被拋出,for-in、生成式(comprehension)、元組解壓(tuple unpacking)等迭代操作都會(huì)處理并這個(gè)異常。
迭代器是個(gè)迭代值生產(chǎn)工廠,它保存迭代狀態(tài),并通過next()函數(shù)產(chǎn)生下一個(gè)迭代值。實(shí)現(xiàn)迭代器需要實(shí)現(xiàn)以下兩個(gè)方法:
__iter__
返回self
__next__
返回下一個(gè)可用的元素,如果無可用元素則拋出StopIteration異常
迭代器實(shí)現(xiàn)__iter__,因此所有的迭代器都是可迭代的,下圖展示了iterable和iterator的結(jié)構(gòu)。

迭代器模式
實(shí)現(xiàn)一個(gè)自定義的迭代器模式需要兩個(gè)類,分別為實(shí)現(xiàn)了__iter__方法的類和通過__iter__返回的迭代器實(shí)例類(實(shí)現(xiàn)了__iter__和__next__方法)。下面例子簡單實(shí)現(xiàn)了上述功能。
class IterText:
def __init__(self, text):
self.text = text
def __iter__(self):
return IteratorText(self.text)
class IteratorText:
def __init__(self, text):
self.text = text
self.index = 0
def __iter__(self):
return self
def __next__(self):
try:
letter = self.text[self.index]
except IndexError:
raise StopIteration
self.index += 1
return letter
text = IterText("hey")
for l in text:
print(l)
可迭代的IterText實(shí)現(xiàn)了__iter__方法,返回了迭代器IteratorText實(shí)例。IteratorText實(shí)現(xiàn)了__next__方法返回下一個(gè)迭代元素直到拋出異常,同時(shí)IteratorText實(shí)現(xiàn)了__iter__方法返回自身對(duì)象用于迭代。
這里的IterText和IteratorText很容易混淆,如果在IterText中實(shí)現(xiàn)了__next__方法并將__iter__中返回自身實(shí)例self也可以實(shí)現(xiàn)上述功能,但通常可迭代對(duì)象和迭代器應(yīng)當(dāng)分開,這樣在可迭代對(duì)象中的__iter__中可以返回不同的迭代器對(duì)象,使功能獨(dú)立。
生成器(generator)
通過上述文章說明,迭代器通過next()不斷產(chǎn)出下一個(gè)元素直到迭代器耗盡,而Python中的生成器可以理解為一個(gè)更優(yōu)雅的迭代器(不需要實(shí)現(xiàn)__iter__和__next__方法),實(shí)現(xiàn)了迭代器協(xié)議,它也可以通過next()產(chǎn)出元素。
Python中的生成器主要分為兩種類型:
生成器函數(shù)(generator function)返回得到的生成器:
包含yield關(guān)鍵字的函數(shù)稱為生成器函數(shù)
def gen_func(): yield 1 yield 2 yield 3 g = gen_func()
生成器表達(dá)式(generator expression)返回得到的生成器
g = (i for i in (1, 2, 3))
我們可以利用生成器進(jìn)行迭代操作:
for e in g: print(e) ## 生成器g已被耗盡,如果需要重新迭代需要重新獲得新的生成器對(duì)象 g = gen_func() for e in g: print(e)
利用生成器代替可迭代中的__iter__迭代器
在迭代器模式章節(jié)中,我們在可迭代IterText中的__iter__返回迭代器IteratorText實(shí)例,然而使用生成器的方式會(huì)使代碼更加優(yōu)雅。
class IterText:
def __init__(self, text):
self.text = text
def __iter__(self):
for letter in self.text:
yield letter
因?yàn)閥ield存在于__iter__,因此__iter__變成了生成器函數(shù),調(diào)用它測返回一個(gè)生成器,同時(shí)生成器又實(shí)現(xiàn)了迭代器協(xié)議,因此IterText滿足了可迭代的需求。
總結(jié)
本篇介紹了Python中的可迭代(iterable)、迭代器(iterator)以及它們的關(guān)系,并講述了迭代器模式的實(shí)現(xiàn),同時(shí)通過Python中的生成器完善了迭代器模式。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
python web框架中實(shí)現(xiàn)原生分頁
這篇文章主要為大家詳細(xì)介紹了python web框架中使用原生分頁的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-09-09
淺談Python實(shí)現(xiàn)Apriori算法介紹
這篇文章主要介紹了淺談Python實(shí)現(xiàn)Apriori算法介紹,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-12-12
python設(shè)置隨機(jī)種子實(shí)例講解
在本篇文章里小編給大家整理的是關(guān)于python設(shè)置隨機(jī)種子的相關(guān)知識(shí)點(diǎn)以及實(shí)例內(nèi)容,需要的朋友們學(xué)習(xí)下。2019-09-09
python異常處理之try finally不報(bào)錯(cuò)的原因
這篇文章主要介紹了python異常處理之try finally不報(bào)錯(cuò)的原因,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05
利用Python?Matlab繪制曲線圖的簡單實(shí)例
們經(jīng)常會(huì)遇到這種情況,有一個(gè)數(shù)學(xué)函數(shù),我們希望了解他的圖像,這個(gè)時(shí)候使用python 的matplotlib就可以幫助我們,下面這篇文章主要介紹了利用Python?Matlab繪制曲線圖的相關(guān)資料,需要的朋友可以參考下2021-12-12
Python中Numpy與TensorFlow版本兼容問題完美解決辦法
這篇文章主要給大家介紹了關(guān)于Python中Numpy與TensorFlow版本兼容問題的完美解決辦法,確保Python版本與TensorFlow版本兼容是首要任務(wù),因?yàn)椴患嫒莸慕M合可能導(dǎo)致導(dǎo)入錯(cuò)誤或其他運(yùn)行時(shí)問題,需要的朋友可以參考下2024-07-07
Python基于百度AI實(shí)現(xiàn)抓取表情包
本文先抓取網(wǎng)絡(luò)上的表情圖像,然后利用百度 AI 識(shí)別表情包上的說明文字,并利用表情文字重命名文件,感興趣的小伙伴們可以參考一下2021-06-06

