徹底理解Iterable、Iterator、generator

圖片來自 unsplash

1 Iterable

我們一般稱Iterable可迭代對象。Python 中任意的對象,只要它定義了可以返回一個迭代器的__iter__方法,或者定義了可以支持下標索引的__getitem__方法,那么它就是一個可迭代對象。我們常用到的集合數據類型都是 Iterable。例如列表(list)、元組(tuple)、字典(dict)、集合(set)、字符串(str)等。

我定義了一個列表 numlist,打印出該列表的方法。

numlist = [1, 2, 3]
print(numlist)
print(numlist.__iter__)      # 調用__iter__方法
print(numlist.__getitem__)  # 調用__getitem__方法

運行結果如下:


點擊查看大圖

根據運行結果,我們可知列表就是個可迭代對象。Python 的collections庫有個isinstance()函數。可以用來判斷一個對象是否是 Iterable 對象。

from collections import Iterable  

isinstance({}, Iterable)  
isinstance((), Iterable) 
isinstance(999, Iterable)

運行結果為:


點擊查看大圖

如果我們每次都要使用這個函數來判斷一個對象是否為可迭代對象,這樣操作有點麻煩。有沒有快速判定的方法呢?答案是肯定的。可以直接使用 for 循環進行遍歷的對象就是可迭代對象。

除此之外,generator(生成器)帶 yield 的 generator function 也是可迭代的對象。

2 Iterator

Iterator是迭代器的意思。任意對象,只要定義了next()(Python 2 版本)或者__next__()(Python 3 版本) 方法,那么它就是一個迭代器。迭代器中還有另一個函數__iter__(),它和 next() 方法形成迭代器協議。

  • iter()
    返回主要是返回迭代器對象本身,即return self。如果你自己定義個迭代器,實現該函數就能使用for ... in ...語句遍歷了。

  • next()
    獲取容器中的下一個元素,當沒有可訪問元素后,就拋出StopIteration異常。

遍歷迭代器有兩個方式。一種是使用 next() 函數;另一種則是使用 for each 循環,本質上就是通過不斷調用 next() 函數實現的。

from collections import Iterator

numlist = [1, 2, 3]

# 將數組轉化為迭代器
ite1 = iter(numlist)
print(ite1)

for i in ite1:
    print(i)

print("=========")

ite2 = iter(numlist)
while True:
    try:
        num = ite2.__next__()
        print(num)
    except StopIteration:
        break

值得注意的是一個 Iterator 只能遍歷一次。

3 generator

generator 翻譯成中文是生成器。生成器也是一種特殊迭代器。它其實是生成器函數返回生成器的迭代,“生成器的迭代器”這個術語通常被稱作”生成器”。yield 是生成器實現__next__()方法的關鍵。它作為生成器執行的暫停恢復點,可以對 yield 表達式進行賦值,也可以將 yield 表達式的值返回。任何包含 yield 語句的函數被稱為生成器。

yield是一個語法糖,內部實現支持了迭代器協議,同時yield內部是一個狀態機,維護著掛起和繼續的狀態。

個人認為,生成器算是 Python 非常棒的特性。它的出現能幫助大大節省些內存空間。假如我們要生成從 1 到 10 這 10 個數字,采用列表的方式定義,會占用 10 個地址空間。采用生成器,只會占用一個地址空間。因為生成器并沒有把所有的值存在內存中,而是在運行時生成值。所以生成器只能訪問一次。

創建一個從包含 1 到 10 的生成器的例子。

gen = (i for i in range(10))
print(gen)
for i in gen:
    print(i)

運行結果如下:


點擊查看大圖

帶有 yield 關鍵字 的例子。重點關注運行結果,這能讓你對 yield 有更深的認識。

def testYield(n):
    for i in range(n):
        print("當前值: ", i)
        yield doubeNumber(i)
        print("第 ", i, " 次運行")
    print("testYield 運行結束")

def doubeNumber(i):
    return i*2
    
if __name__ == '__main__':
    for i in testYield(3):
        print(i, "===", i)

運行結果如下:


點擊查看大圖

上篇閱讀:Python 繪圖,我只用 Matplotlib(二)
推薦閱讀:爬蟲系列的總結


最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容