迭代器 & 生成器

微信公眾號搜索【程序媛小莊】,關(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>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容