一、可迭代對象和迭代器
1.迭代的概念
上一次輸出的結(jié)果為下一次輸入的初始值,重復(fù)的過程稱為迭代,每次重復(fù)即一次迭代末贾,并且每次迭代的結(jié)果是下一次迭代的初始值
注:循環(huán)不是迭代
while True: #只滿足重復(fù)蜓洪,因而不是迭代
print('====>')
2.可迭代的對象
內(nèi)置__iter__方法的皮服,都是可迭代的對象。
list是可迭代對象着饥,dict是可迭代對象犀农,set也是可迭代對象。
[1,2].__iter__()
'hello'.__iter__()
(1,2).__iter__()
{'a':1,'b':2}.__iter__()
{1,2,3}.__iter__()
例如:
x = [1, 2, 3]
y = iter(x)
z = iter(x)
print(next(y))
print(next(y))
print(next(z))
print(type(x))
print(type(y))
輸出
1
2
1
;
3.迭代器
·1.為什么要有迭代器宰掉?
對于沒有索引的數(shù)據(jù)類型呵哨,必須提供一種不依賴索引的迭代方式赁濒。
·2.迭代器定義:
迭代器:可迭代對象執(zhí)行__iter__方法,得到的結(jié)果就是迭代器孟害,迭代器對象有__next__方法
它是一個帶狀態(tài)的對象拒炎,他能在你調(diào)用next()方法的時候返回容器中的下一個值,任何實現(xiàn)了__iter__和__next__()方法的對象都是迭代器纹坐,__iter__返回迭代器自身枝冀,__next__返回容器中的下一個值,如果容器中沒有更多元素了耘子,則拋出StopIteration異常
·3.迭代器的實現(xiàn)
例:
i=[1,2,3].__iter__()
print(i) #迭代器
print(i.__next__())
print(i.__next__())
print(i.__next__())
#print(i.__next__()) #拋出異常:StopIteration
輸出
1
2
3
每次調(diào)用next()方法的時候做兩件事:
00001.為下一次調(diào)用next()方法修改狀態(tài)
00002.為當(dāng)前這次調(diào)用生成返回結(jié)果
迭代器就像一個懶加載的工廠果漾,等到有人需要的時候才給它生成值返回,沒調(diào)用的時候就處于休眠狀態(tài)等待下一次調(diào)用谷誓。
·4.如何判斷迭代器對象和可迭代對象
from collections importIterable,Iterator
'abc'.__iter__()
().__iter__()
[].__iter__()
{'a':1}.__iter__()
{1,2}.__iter__()
f=open('a.txt','w')
f.__iter__()
#判斷是否為可迭代對象绒障,以下都是
print(isinstance('abc',Iterable))
print(isinstance([],Iterable))
print(isinstance((),Iterable))
print(isinstance({'a':1},Iterable))
print(isinstance({1,2},Iterable))
print(isinstance(f,Iterable))
#判斷是否為迭代器,只有文件是
print(isinstance('abc',Iterator))
print(isinstance([],Iterator))
print(isinstance((),Iterator))
print(isinstance({'a':1},Iterator))
print(isinstance({1,2},Iterator))
print(isinstance(f,Iterator))
輸出
True
True
True
True
True
True
False
False
False
False
False
True
可迭代對象:只有__iter__方法捍歪,執(zhí)行該方法得到的迭代器對象
迭代器:有__iter__和__next__()方法
注:對于迭代器對象來說户辱,執(zhí)行__iter__方法,得到的結(jié)果仍然是它本身
·5.迭代器的優(yōu)點和缺點
優(yōu)點:
1.提供了一種不依賴下標的迭代方式
2.就跌迭代器本身來說糙臼,更節(jié)省內(nèi)存
缺點:
1. 無法獲取迭代器對象的長度
2. 不如序列類型取值靈活庐镐,是一次性的,只能往后取值变逃,不能往前退
二必逆、生成器
1.定義
生成器(generator)是一個特殊的迭代器,它的實現(xiàn)更簡單優(yōu)雅揽乱,yield是生成器實現(xiàn)__next__()方法的關(guān)鍵名眉。它作為生成器執(zhí)行的暫停恢復(fù)點凰棉,可以對yield表達式進行賦值损拢,也可以將yield表達式的值返回。
也就是說撒犀,yield是一個語法糖福压,內(nèi)部實現(xiàn)支持了迭代器協(xié)議,同時yield內(nèi)部是一個狀態(tài)機或舞,維護著掛起和繼續(xù)的狀態(tài)隧膏。
yield的功能:
1.相當(dāng)于為函數(shù)封裝好__iter__和__next__
2.return只能返回一次值,函數(shù)就終止了嚷那,而yield能返回多次值胞枕,每次返回都會將函數(shù)暫停,下一次next會從上一次暫停的位置繼續(xù)執(zhí)行
例:
defcounter(n):
print('start...')
i=0
while i < n:
yield i
i+=1
print('end...')
g=counter(5)
print(g)
print(next(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g))
# print(next(g)) ??#會報錯
輸出
start...
0
1
2
3
4
2.生成器函數(shù)
·生成器函數(shù):常規(guī)函數(shù)定義魏宽,但是腐泻,使用yield語句而不是return語句返回結(jié)果决乎。yield語句一次返回一個結(jié)果,在每個結(jié)果中間派桩,掛起函數(shù)的狀態(tài)构诚,以便下次重它離開的地方繼續(xù)執(zhí)行擦耀;
普通函數(shù)return返回
deflay_eggs(num):
egg_list=[]
for egg in range(num):
egg_list.append('蛋%s' %egg)
return egg_list
yikuangdan=lay_eggs(10) #我們拿到的是蛋
print(yikuangdan)
輸出
['蛋0', '蛋1', '蛋2', '蛋3', '蛋4', '蛋5', '蛋6', '蛋7', '蛋8', '蛋9']
迭代器函數(shù)
deflay_eggs(num):
for egg in range(num):
res='蛋%s' %egg
yield res ??????#生成器關(guān)鍵語法
print('下完一個蛋')
laomuji=lay_eggs(10) #我們拿到的是一只母雞
print(laomuji)
print(laomuji.__next__()) ??????#迭代 ?蛋0
print(laomuji.__next__()) ????#蛋1
print(laomuji.__next__()) ??????#蛋2
egg_l=list(laomuji)
print(egg_l)
輸出
蛋0
下完一個蛋
蛋1
下完一個蛋
蛋2
下完一個蛋
下完一個蛋
下完一個蛋
下完一個蛋
下完一個蛋
下完一個蛋
下完一個蛋
下完一個蛋
['蛋3', '蛋4', '蛋5', '蛋6', '蛋7', '蛋8', '蛋9']
3.生成器表達式
·生成器表達式:類似于列表推導(dǎo)拆挥,但是,生成器返回按需產(chǎn)生結(jié)果的一個對象普碎,而不是一次構(gòu)建一個結(jié)果列表员魏;
·
food=yield food_list
·
#g.send('food1'),先把food1傳給yield丑蛤,由yield賦值給food,然后返回給food_list,然后再往下執(zhí)行撕阎,直到再次碰到y(tǒng)ield受裹,然后把yield后的返回值返回給food_list
·
例
注意:開始生成器不能send非空值
def eater(name): ???????#協(xié)程函數(shù)
print('%s ready to eat' %name)
food_list=[]
while True:
food=yield food_list ??????????#裝飾器表達式 ???????food_list.append(food)
print('%s start to eat %s' %(name,food))
g=eater('hexin')
print(g) ???????#生成器
print(g.send('food1')) ?#傳值
輸出
Traceback (most recent call last):
#生成器對象
File "/Users/hexin/PycharmProjects/py3/day5/2.py", line 71, in
print(g.send('food1'))
TypeError: can't send non-None value to a just-started generator #開始生成器不能send非空值
·初始化后
def eater(name): ???????#協(xié)程函數(shù)
print('%s ready to eat' %name)
food_list=[]
while True:
food=yield food_list ??????????#裝飾器表達式 ???????food_list.append(food)
print('%s start to eat %s' %(name,food))
g=eater('hexin')
print(g) ???????#生成器
next(g) #等同于 g.send(None),初始化
print(g.send('food1'))
輸出
hexin ready to eat
hexin start to eat food1
['food1']
·為了防止忘記初始化虏束,可利用裝飾器進行初始化棉饶,如下
def deco(func): ????#初始化函數(shù)
def wrapper(*args,**kwargs):
res=func(*args,**kwargs)
next(res) ?????????#等同于 g.send(None),初始化
return res
return wrapper
@deco ??????#用初始化函數(shù)裝飾器,調(diào)用初始化函數(shù)
def eater(name): ???????#協(xié)程函數(shù)
print('%s ready to eat' %name)
food_list=[]
while True:
food=yield food_list ??????????#裝飾器表達式 ???????food_list.append(food)
print('%s start to eat %s' %(name,food))
g=eater('hexin')
# print(g) ???????#生成器
# next(g) #等同于 g.send(None)镇匀,初始化
print(g.send('food1'))
print(g.send('food2'))
print(g.send('food3'))
輸出
hexin ready to eat
hexin start to eat food1
['food1']
hexin start to eat food2
['food1', 'food2']
hexin start to eat food3
['food1', 'food2', 'food3']