可迭代對象侣诵、迭代器痢法、生成器、生成式區(qū)別
a. 可迭代對象:
一個(gè)對象能夠被迭代的使用杜顺,這個(gè)對象就是可迭代對象
容器是一種把多個(gè)元素組織在一起的數(shù)據(jù)結(jié)構(gòu)财搁,容器中的元素可以逐個(gè)的迭代獲取,容器本身實(shí)際
上并不支持取出元素的功能哑舒,而是由可迭代對象賦予了容器這種能力妇拯,比如列表中的元素獲取,元
祖、字典越锈、集合等仗嗦;
python數(shù)據(jù)類型中,除了整型的基本數(shù)據(jù)類型都是可迭代對象甘凭,包括文件對象稀拐,python內(nèi)部定義一個(gè)對象是不是可迭代對象的依據(jù)是,該對象是否存在__iter__()對象方法丹弱。
所以判斷一個(gè)對象是否是可迭代對象的方法有:
方法一:
from collections import Iterable
isinstance(obj,Iterable) # 返回True表明是可迭代對象
方法二:
hasattr(obj,'__iter__') # 返回True表明是可迭代對象
因此:可以通過添加__iter__()方法讓一個(gè)類的實(shí)列變?yōu)榭傻鷮ο?
b. 迭代器:
迭代器也是一種容器德撬,并且是可迭代對象,因?yàn)榈魇怯衉iter_()方法的躲胳,迭代器與可迭代對象的區(qū)別就在于蜓洪,迭代器有_next_()方法,而單純的可迭代對象并沒有這個(gè)方法
判斷對象是是否是迭代器:
方法一:
from collections import iterator
isinstance(boj,Iterator) # 返回True表明是迭代器
方法二:
hasattr(obj,'__next__') # 返回True表明是迭代器
迭代器可以通過內(nèi)置函數(shù)next(obj)和obj.__next__()方法獲取迭代器的下一個(gè)值坯苹,當(dāng)?shù)鞯闹?取完了之后隆檀,再取會(huì)拋出StopIteration錯(cuò)誤,但是可迭代對象并不能使用這兩個(gè)方法
c. 生成器(本質(zhì)上來說就是一個(gè)迭代器):
一個(gè)利用yield返回結(jié)果的函數(shù)就是一個(gè)生成器粹湃,用iter(iterable)也可以生成一個(gè)生成器
一般的函數(shù)在執(zhí)行完畢之后會(huì)返回一個(gè)值然后退出恐仑,但是生成器函數(shù)會(huì)自動(dòng)掛起,待下一次調(diào)用時(shí)为鳄,
會(huì)在上次結(jié)束位置繼續(xù)執(zhí)行裳仆,實(shí)現(xiàn)了延遲計(jì)算,省內(nèi)存
斐波那契數(shù)列:
def fib(max):
n,a,b =0,0,1
while n < max:
yield b
a,b =b,a+b
n = n+1
return 'done'
a = fib(10) # 先調(diào)用生成器函數(shù)保存為迭代器對象,再獲取元素孤钦,如果直接next(fib(10))歧斟,無論多少次next,只會(huì)拿到第一次元素
for i in a:
print(i)
d.生成式
生成式是一種簡單的生成器,返回一個(gè)迭代器對象偏形,來源于迭代和列表解析的組合
列表解析式:
a = [i for i in range(10)]
生成式:
b = (i for i in range(10))
a和b主要有兩點(diǎn)區(qū)別构捡,第一就是a占用的內(nèi)存比b大,第二就是a是通過遍歷或者下標(biāo)
獲取元素壳猜,b是通過遍歷或者next獲取元素
e. for循環(huán)的遍歷機(jī)制:
可迭代對象是不可以直接從其中獲取元素的,for i in obj遍歷obj對象時(shí)滑凉,在for循環(huán)內(nèi)部统扳,被遍歷
的對象obj會(huì)首先調(diào)用__iter__()方法,將其變?yōu)橐粋€(gè)迭代器畅姊,然后這個(gè)迭代器再調(diào)用其__next__()
方法咒钟,返回取到的值給i,簡單的說若未,for i in obj這句代碼做的事就是:
obj_iter = obj.__iter__()
i = obj_iter.__next__()
當(dāng)然以上代碼功能并不完整朱嘴,因?yàn)閒or循環(huán)還自動(dòng)捕捉了迭代器元素取完之后的StopIteration錯(cuò)誤
完整模擬for循環(huán)的內(nèi)部機(jī)制的代碼如下:
l = [1,2,3,4,5]
item = l.__iter__() # 生成一個(gè)迭代器
while True:
try:
i = item.__next__()
print(i)
except StopIteration: # 捕獲異常,如果有異常,說明應(yīng)該停止迭代
break
f. 反向迭代和迭代器切片操作:
(1)反向迭代:
python內(nèi)置函數(shù)revered()可以實(shí)現(xiàn)可迭代對象的反向迭代:
a = [1,2,3,4]
b = reversed(a) # 此方法是生成一個(gè)a的反向?qū)ο笃兼遥瑒?chuàng)建新的對象
print(b) # 輸出:[4,3,2,1]
等同于:
a.reverse() # 此方法是將a本身反向乌昔,并不創(chuàng)建新對象(只能引用于可變對象)
如果實(shí)現(xiàn)了__reversed__()方法,就可以在自定義的類上實(shí)現(xiàn)反向迭代
(2)迭代器切片:
切片(islice方法):
import itertools
a = (i for i in range(10))
b = itertools.islice(a,3) # 返回一個(gè)迭代器對象,類似 [:3]
c = itertools.islice(a,3,None) # 返回一個(gè)迭代器對象壤追,類似 [3:]
c = itertools.islice(a,3,6) # 類似 [3:6]
注意: islice會(huì)消耗迭代器磕道,經(jīng)過切片的迭代器會(huì)將切片部分以及切片之前的元素去掉,
設(shè)置了None的islice會(huì)將原迭代器全部消耗掉:
b = (i for i in range(10)) # 如果b是列表行冰,用islice不會(huì)消耗該列表
c = itertools.islice(b,3,None)
print([i for i in c]) # 輸出 [3, 4, 5, 6, 7, 8, 9]
print(next(b)) # 拋出StopIteration錯(cuò)誤
去特定元素(dropwhile方法):
import itertools
with open('test.py','r') as f:
for line in itertools.dropwhile(lambda x:x.startswith('#'), f): # 遍歷不是以#開頭的所有行
print(line)
其中:itertools.dropwhile(lambda x:x.startswith('#'), f)表示刪除f中以#開頭的行溺蕉,返回其他行的迭代器