無(wú)論是面試測(cè)試還是運(yùn)維涉及到python編碼崗位時(shí),迭代器和可迭代對(duì)象都是繞不開(kāi)的一個(gè)問(wèn)題,本文對(duì)這兩個(gè)概念進(jìn)行重點(diǎn)講解,本文從什么是迭代講起,然后介紹迭代器和可迭代對(duì)象二者的區(qū)別,最后通過(guò)for 循環(huán)和自定義迭代器來(lái)加深讀者對(duì)這兩個(gè)概念的理解,只要認(rèn)真閱讀完文章,相信一定會(huì)幫助到大家,文章有點(diǎn)長(zhǎng),建議首收藏!
關(guān)于迭代,維基百科是這樣子定義的:迭代是重復(fù)反饋過(guò)程的活動(dòng),其目的通常是為了接近并到達(dá)所需的目標(biāo)或結(jié)果。每一次對(duì)過(guò)程的重復(fù)被稱(chēng)為一次“迭代”,而每一次迭代得到的結(jié)果會(huì)被用來(lái)作為下一次迭代的初始值。
在程序中,迭代是一種遍歷集合元素的方式,我們最常用的迭代應(yīng)用如下:
for i in [1,2,3]: print(i)
輸出:
1
2
3
只要實(shí)現(xiàn) __ iter __ 方法或者實(shí)現(xiàn) __ getitem __方法而且其參數(shù)從0開(kāi)始索引,那么該對(duì)象就是可迭代對(duì)象(iterable)。
Python 中的大多數(shù)內(nèi)置數(shù)據(jù)結(jié)構(gòu)(容器)都是可迭代對(duì)象,比如list、dict、tuple、set、string。創(chuàng)建一個(gè)可迭代對(duì)象的實(shí)例如下:
class IterableDemo(object): def __init__(self, components): self.components = list(components) def __iter__(self): return iter(self.components)V1 = IterableDemo([1, 2, 3])for i in V1: print(i)
輸出
1
2
3
可以看到實(shí)例IterableDemo進(jìn)行了迭代,該對(duì)象之所以能迭代,是因?yàn)閷?shí)現(xiàn)了__ iter __ ()方法。當(dāng)使用for循環(huán)時(shí)候,解釋器會(huì)檢查對(duì)象是否有__ iter __ ()方法,有的話就是調(diào)用它來(lái)獲取一個(gè)迭代器。所以沒(méi)有 __ iter __ ()方法但實(shí)現(xiàn)了__ getitem __ (),解釋器會(huì)創(chuàng)建一個(gè)迭代器,嘗試從0開(kāi)始按順序遍歷元素。如果嘗試失敗,Python便會(huì)拋出TypeError錯(cuò)誤。
看下面這個(gè)list的例子,l=[1,2,3]。這里 l是一個(gè)可迭代對(duì)象。
我們可以通過(guò)方法__iter__()和函數(shù)iter把list(可迭代對(duì)象)轉(zhuǎn)變成list迭代器對(duì)象,代碼如下
print(type(l))print(type(l.__iter__()))print(type(iter(l)))
輸出
<class 'list'>
<class 'list_iterator'>
<class 'list_iterator'>
我們可以看到,list變成了list_iterator,證明list是可迭代對(duì)象!
a=123456
print(type(iter(a)))
輸出
TypeError: 'int' object is not iterable
可見(jiàn)int 類(lèi)型是不可迭代對(duì)象,也就是說(shuō)調(diào)用iter(對(duì)象)函數(shù),如果該對(duì)象不可迭代,就會(huì)拋出TypeError的錯(cuò)誤。
Python 中的迭代器是一個(gè)可以迭代的對(duì)象,一個(gè)每次僅僅返回一個(gè)元素的對(duì)象。從技術(shù)上講,Python 迭代器對(duì)象必須實(shí)現(xiàn)兩個(gè)魔法方法:__iter__() 和 __next__()方法,統(tǒng)稱(chēng)為迭代器協(xié)議(iterator protocol)。
容器是一種把多個(gè)元素組織在一起的數(shù)據(jù)結(jié)構(gòu),容器中的元素可以逐個(gè)地迭代獲取,可以用 in , not in 關(guān)鍵字判斷元素是否包含在容器中。通常這類(lèi)數(shù)據(jù)結(jié)構(gòu)把所有的元素存儲(chǔ)在內(nèi)存中(也有一些特列并不是所有的元素都放在內(nèi)存)在Python中,常見(jiàn)的容器對(duì)象有:list、dequeue、set、dict、Counter、tuple、str等等。盡管絕大多數(shù)容器都提供了某種方式來(lái)獲取其中的每一個(gè)元素,但這并不是容器本身提供的能力,而是 可迭代對(duì)象 賦予了容器這種能力,當(dāng)然并不是所有的容器都是可迭代的(Bloom filter容器不可以迭代)。我們執(zhí)行以下代碼,創(chuàng)建x 和y兩個(gè)獨(dú)立的迭代器。
l=[1,2,3]x=iter(l)y=iter(l)print('y第一次調(diào)用:'+str(next(y)))print('x第一次調(diào)用:'+str(next(x)))print('y第二次調(diào)用:'+str(next(y)))print('x第二次調(diào)用:'+str(next(x)))
輸出
y第一次調(diào)用:1
x第一次調(diào)用:1
y第二次調(diào)用:2
x第二次調(diào)用:2
證明了
1. x、y是兩個(gè)獨(dú)立的迭代器,彼此不影響
2. 迭代器內(nèi)部持有一個(gè)狀態(tài),該狀態(tài)用于記錄當(dāng)前迭代所在的位置,調(diào)用next,每次都是按順序獲取對(duì)應(yīng)的元素
那么究竟什么是迭代器呢?
迭代器是一個(gè)可以記住遍歷位置的對(duì)象,其內(nèi)部有一個(gè)狀態(tài)用于記錄迭代所在的位置,以便下次迭代時(shí)候能取出正確的元素。迭代器就像一個(gè)懶人一樣,當(dāng)你需要數(shù)據(jù)時(shí)候才會(huì)返回給你,否則就在等待下一次的調(diào)用。
迭代器技術(shù)主要應(yīng)用在哪些地方呢?主要包括:
· for 循環(huán)
· 構(gòu)建和擴(kuò)展集合類(lèi)型
· 逐行遍歷文本文件
· 列表推導(dǎo)、字典推導(dǎo)和集合推導(dǎo)
· 元組拆包
· 調(diào)用函數(shù)時(shí),使用*拆包
共性:他們都可以通過(guò)循環(huán)的形式進(jìn)行迭代;
迭代器是可迭代對(duì)象, 但是可迭代對(duì)象不一定是迭代器,例如list是可迭代對(duì)象,但不是迭代器;
可以通過(guò)iter(可迭代對(duì)象)的方法來(lái)生成迭代器;
可迭代對(duì)象需要實(shí)現(xiàn) __ iter __ 方法或者實(shí)現(xiàn) __ getitem __方法而且其參數(shù)從0開(kāi)始索引。注意可迭代對(duì)象不一定實(shí)現(xiàn)__ next__方法;
迭代器需要同時(shí)實(shí)現(xiàn)__ iter __ 方法和__next__ 方法,當(dāng)使用next()函數(shù)時(shí)會(huì)調(diào)用__next__方法。
舉一個(gè)經(jīng)典的例子把
l=[1,2,3]print(next(l))
輸出error:
print(next(l))
TypeError: 'list' object is not an iterator
可見(jiàn),list不是一個(gè)迭代器,因?yàn)樗麤](méi)有實(shí)現(xiàn)__next__方法
接下來(lái)執(zhí)行代碼,print(next(iter(l)))
輸出
1
可見(jiàn)通過(guò)iter(l) 生成了迭代器,因此next操作可以順利進(jìn)行
在大多數(shù)情況下,我們不會(huì)一次次調(diào)用next方法去取值,而是通過(guò) for i in (iterable)的方式,如下圖:
循環(huán)背后的原理又是什么呢?
調(diào)用iter(容器對(duì)象,即可迭代對(duì)象,即上圖中的x=[1,2,3])函數(shù)生成迭代器(如果調(diào)用成功)
通過(guò)迭代器的__next__()方法訪問(wèn)容器元素
如果沒(méi)有元素了,將拋出StopIteration exception
循環(huán)捕獲StopIteration exception后就會(huì)終止循環(huán)
如果想自定義迭代器,需要實(shí)現(xiàn)__iter_和__next__兩個(gè)方法
from collections.abc import Iterableclass MyIterator: last = 0 def __iter__(self): return self def __next__(self): self.last += 1 if self.last > 5: raise StopIteration return self.lastmy= MyIterator()print(isinstance(my,Iterable))for i in my:print(i)
輸出:
True
1
2
3
4
5
從輸出結(jié)果可以看出my 是iterable對(duì)象,并完成了遍歷工作!
大家可以自定義一個(gè)類(lèi),里面不實(shí)現(xiàn)__iter_和__next__兩個(gè)方法,例如
class Demo: passdemo= Demo()print(type(iter(demo)))
輸出:
TypeError: 'Demo' object is not iterable
聯(lián)系客服