微信公眾號搜索【程序媛小莊】,關(guān)注半路出家的程序媛如何靠python開發(fā)養(yǎng)家糊口~
前言
字符串、列表、元組、字典、集合都屬于可迭代對象,本文將會介紹什么是可迭代對象,擦亮blingbling的大眼睛一起來看吧!
迭代器對象
迭代器是用來迭代取值的工具,迭代是一個重復(fù)的過程,但是每次重復(fù)都是基于上一次的結(jié)果繼續(xù)的,單純的重復(fù)不是迭代,如下述代碼:
while True:
info = input('your name>>>')
print(info)
下述代碼屬于迭代過程,不僅重復(fù)循環(huán),每次重復(fù)index都是基于上次的結(jié)果:
best_language = ['python', 'java', 'c']
index = 0
while index < len(best_language):
print(best_language[index])
index += 1
上述方法是根據(jù)索引來取值的,也可以稱為迭代取值,但是對于沒有索引的數(shù)據(jù)類型比如字典,如何取值呢?所以python解釋器必須提供一種不依賴索引取值的方式,這種方式就是迭代器。
而要想知道什么是迭代器需要先知道什么是可迭代對象,一起來看看吧!
可迭代對象(iterable)
在前面介紹數(shù)據(jù)類型的時候說到可迭代對象是這樣定義的 - 能夠被for循環(huán)的對象就是可迭代對象,可迭代對象有字符串、列表、元組、字典、集合、打開的文件等,其實(shí)可迭代對象有更具體的定義方式 - 但凡內(nèi)置方法中有__iter__方法的對象就是可迭代對象。
# 文件對象屬于可迭代對象
with open(r'F:\FullStack\Python_based\info') as f:
f.__iter__()
調(diào)用可迭代對象中的__iter__方法返回的結(jié)果就是一個迭代器對象,迭代器對象也有更加具體的定義方式 - 但凡內(nèi)置方法中有__iter__和__next__方法的對象就是迭代器對象。。
比如打開的文件本身就是一個迭代器對象,執(zhí)行迭代器對象的__iter__方法得到的仍然是迭代器對象,執(zhí)行迭代器對象的__next__方法就會得到迭代器對象中的下一個值。
執(zhí)行迭代器對象的__next__方法時,當(dāng)?shù)髦械闹当蝗⊥昃蜁伋鯯topIteration的異常,迭代器可以理解為一只會下蛋的母雞,假設(shè)這只母雞一輩子只能下3個雞蛋,那么下完這三個蛋之后,母雞就會死翹翹~無法繼續(xù)下蛋。
>>> list1 = [1, 2, 3]
>>> iterator = list1.__iter__() # 等價于 iterator = iter(list1)
>>> iterator.__next__() # 等價于 next(iterator)
1
>>> iterator.__next__()
2
>>> iterator.__next__()
3
>>> iterator.__next__()
Traceback (most recent call last):
File "<stdin>", line 1, in <module> StopIteration
可以用循環(huán)實(shí)現(xiàn)上面重復(fù)的輸出:異常處理(后面文章會詳細(xì)介紹),當(dāng)出現(xiàn)異常的時候會終止循環(huán),不會報錯。
list1 = [1, 2, 3]
iterator = list1.__iter__()
while True:
try: # try except是捕捉異常的語法
print(iterator.__next__())
except StopIteration:
break
# 再次對迭代器進(jìn)行循環(huán)取值
while True:
try:
print(iterator.__next__()) # 這里是取不到值的
except StopIteration:
break
這里會產(chǎn)生一個問題,再次對迭代器進(jìn)行循環(huán)取值的時候,已經(jīng)無法取到迭代器的值了,原因就是相當(dāng)于迭代器這只母雞下完蛋之后就死掉了,再讓它下蛋你覺得可能嗎?如果還想要在這個迭代器中取值,解決辦法只能再造一個迭代器。
迭代器的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
1.為序列和非序列數(shù)據(jù)類型提供了一種統(tǒng)一的迭代取值方式
2.迭代器對象表示的是一個數(shù)據(jù)流,可以只在需要時才去調(diào)用next來計算出一個值,就迭代器本身來說,同一時刻在內(nèi)存中只有一個值,因而可以存放無限大的數(shù)據(jù)流,而對于其他容器類型,如列表,需要把所有的元素都存放于內(nèi)存中,受內(nèi)存大小的限制,可以存放的值的個數(shù)是有限的。
缺點(diǎn):
1.只能取下一個值,不能回到開始,更像是‘一次性的’,迭代器產(chǎn)生后的唯一目標(biāo)就是重復(fù)執(zhí)行next方法直到值取盡,否則就會停留在某個位置,等待下一次調(diào)用next;
2.若是要再次迭代同個對象,你只能重新調(diào)用__iter__方法去創(chuàng)建一個新的迭代器對象,如果有兩個或者多個循環(huán)使用同一個迭代器,必然只會有一個循環(huán)能取到值。
for循環(huán)原理
for循環(huán)的對象一定是可迭代對象,因此for循環(huán)也被稱為迭代器循環(huán),for循環(huán)的原理就是基于迭代器的。
首先,將for循環(huán)將可迭代對象通過調(diào)用__iter___方法轉(zhuǎn)換成迭代器對象;
然后,調(diào)用迭代器對象的內(nèi)置方法__next__獲取迭代器中的下一個值;
最后,循環(huán)上一步,直到迭代器的值取盡后捕捉異常,終止循環(huán)。
list1 = [1, 2, 3]
iterator = list1.__iter__()
while True:
try:
print(iterator.__next__())
except StopIteration:
break
生成器 & yield
函數(shù)用return關(guān)鍵字來返回值,將函數(shù)體代碼中的return關(guān)鍵字替換成yield關(guān)鍵字,再調(diào)用函數(shù),不會執(zhí)行函數(shù)體代碼,得到的返回值就是一個生成器對象。
得到的生成器(generator)對象有內(nèi)置方法__iter__和__next__,因此生成器本身就是自定義的迭代器。
# 自定義一個可以產(chǎn)生很多數(shù)字的生成器,就像range一樣
def my_range(start, stop, step=1):
while start < stop:
yield start
start += step
res = my_range(1,10,2)
print(res) # <generator object my_range at 0x0000023D283A10B0>
print(res.__iter__()) # <generator object my_range at 0x0000023D283A10B0>
print(res.__next__()) # 1
既然生成器屬于迭代器,那么一定可以使用for循環(huán)對生成器進(jìn)行迭代取值:
for i in my_range(1, 10, 2):
print(i)
將函數(shù)體代碼中的return關(guān)鍵字替換成yield關(guān)鍵字,就有了自定義迭代器的實(shí)現(xiàn)方式,yield不同于return,函數(shù)一旦遇到return就結(jié)束了,但是yield可以保存函數(shù)的運(yùn)行狀態(tài),用來返回多次值。
三元表達(dá)式
需要補(bǔ)充一個知識點(diǎn) - 三元表達(dá)式,它是對分支結(jié)構(gòu)的一種簡化,舉例如下:
def func(x,y):
if x > y:
return x
else:
return y
res = func(1,2)
print(res)
上述代碼中的分支結(jié)構(gòu)就可以使用三元表達(dá)式進(jìn)行簡化,三元表達(dá)式的語法結(jié)構(gòu)如下:
res = 條件成立返回的值 if 條件 else 條件不成立返回的值
三元表達(dá)式是python提供的一種簡化代碼的方案。因此上述代碼可以簡化為:
def func(x, y):
res = x if x > y else y
return res
生成式
生成式和三元表達(dá)式一樣是python提供的一種簡化代碼的方案,可以用來快速生成列表、字典、生成器,生成式有下面幾種:
列表生成式
列表生成式可以快速生成一個列表,語法如下:
list1 = [i for i in iterable if condition] # if語句可有可無,根據(jù)實(shí)際場景
相當(dāng)于:
list1 = []
for i in iterable:
if condition:
list1.append(i)
比如針對下面的需求,可以使用列表生成式一行解決:
# 將下述列表中的元素全部變成大寫
l = ['python', 'java', 'go']
# 方式一:
new_list = []
for i in l:
new_list.append(i.upper())
# 方式二:使用列表生成式可以一行解決
new_list = [i.upper() for i in l]
print(new_list) # ['PYTHON', 'JAVA', 'GO']
字典生成式
字典生成式可以快速初始化一個字典:
key = {'1','2','3'}
dic = {i:None for i in key}
print(dic)
# 結(jié)果如下
{'3': None, '2': None, '1': None}
生成器生成式
創(chuàng)建一個生成器可以使用帶有yield關(guān)鍵字的函數(shù),另一種就是使用生成器生成式,生成器生成式與列表生成式的語法格式相同,只需要將[]替換成().
注意,對比列表生成式返回的是一個列表,生成器表達(dá)式返回的是一個生成器對象。優(yōu)點(diǎn)自然是節(jié)省內(nèi)存,一次只產(chǎn)生一個值存在內(nèi)存中。
>>> [i for i in range(4)]
[0, 1, 2, 3]
>>> (i for i in range(4))
<generator object <genexpr> at 0x0000026E4194BD60>