迭代器、生成器、生成器表達式

本質上,迭代器、生成器和生成器表達式是同一種東西,向Python解釋器不斷的提供對象。

只不過爲了達到同樣的目的,迭代器、生成器和生成器表達式實現的方式有所不同。

迭代器

使用類去實現Python的迭代器協議,重寫__iter__和__next__魔術方法。

其中__iter__函數必須返回一個實現了__next__函數的對象,__next__函數的返回值就是迭代器對象每次提供的對象內容。

如果在迭代過程中,__next__函數拋出了StopIteration異常,則迭代器中止。

'''生成N個1~10之間隨機數字的迭代器
'''
import random

class MyIter:
    def __init__(self,n):
        self.count = 0
        self.ntimes = n

    def __iter__(self):
    '''必須返回一個實現了__next__的對象'''
        return self

    def __next__(self):
    '''函數返回值就是每次迭代時由迭代器提供的對象,
       函數拋出StopIteration時,意味着迭代器的中止
    '''
        if self.count >= self.ntimes:
            raise StopIteration
        else:
            self.count += 1
            return random.randint(1,10)

手動迭代:

myiter = MyIter(5)
itr = iter(myiter) # 內置函數iter會調用myiter.__iter__()
print(next(itr))   # 內置函數next會調用itr.__next__()
print(next(itr))
print(next(itr))
print(next(itr))
print(next(itr))
print(next(itr)) # 第6次調用itr的__next__函數,產生StopIteration異常

也可以使用for循環來從迭代器取內容:

for i in MyIter(5):
    print(i)

屏幕上會打印5個1~10之間的隨機數字,並且for循環會處理StopIteration異常,中止繼續迭代。

爲了提供幾個數字,迭代器卻需要從類開始構建,並實現迭代器協議,有些過於繁瑣。於是就有了生成器。

生成器

生成器是一個函數。與一般函數不同的是,生成器函數多了一個“返回”關鍵字yield,通過yield“返回”的內容是迭代過程中生成器提供給Python解釋器的對象。一旦生成器函數由return返回了,就意味着函數的結束也意味着生成器中止。

'''生成N個1~10之間隨機數字的生成器
'''
import random

def mygen(n):
    for i in range(n):
        yield random.randint(1, 10)

 mygen是一個生成器函數,通過yield“返回”內容時控制流從函數暫時交還給函數調用處。只要迭代繼續進行,控制流就能回到函數中上一次yield返回處繼續執行。當n次後,循環語句結束,mygen函數通過return返回None。mygen函數一旦執行了return,就意味着將控制流徹底的從函數中交還給了函數調用處。至此,產生StopIteration異常,生成器中止。

手動迭代:

gen = mygen(5)    #獲得一個生成器
print(next(gen))   # 打印yield的“返回值”
print(next(gen))
print(next(gen))
print(next(gen))
print(next(gen))
# 第6次調用時,for循環結束
# mygen函數通過return語句返回None
# 函數結束,生成器中止,產生StopIteration異常
print(next(gen)) 

也可以使用for循環來從迭代器取內容:

for i in mygen(5):
    print(i)

屏幕上會打印5個1~10之間的隨機數字,for循環會處理StopIteration異常,中止繼續迭代。

實現同樣功能時,生成器僅僅需要3行代碼就可以做到,可以把生成器函數看做迭代器的語法糖。

生成器表達式

就像列表表達式(也稱爲列表推導式)是生成列表的簡化方式,生成器表達式(也稱爲生成器推導式)是生成器函數的簡化方式。

'''生成5個1~10之間隨機數的生成器表達式
'''
gen = (random.randint(1,10) for i in range(5)) 

表達式的結果是一個生成器對象,通過gen可以手動或for迭代。

這一次代碼簡化到了一行,但是與生成器函數相比有一些不便的地方在於:

1. 顯然生成隨機數的個數不能通過參數傳入,只能寫成固定的數值5

2.再想獲得一個生成器迭代時,只能把表達式重新再寫一遍,不太符合代碼複用的原則。

gen = (random.randint(1, 10) for _i in range(5))
for i in gen:
    print(i)

# 再想使用時,只能把表達式重新再寫一遍
gen2 = (random.randint(1, 10) for _i in range(5))
print(next(gen2))

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章