用程式碼來解釋可迭代性,迭代器,生成器的區別
一. 創造器(creator)
這是我自己造的一個名詞,因為在python術語中,對只實現了__next__()方法的物件,好像沒有任何名分,為了說明,我將只實現了__next__()方法的物件稱為創造器(creator)。
class O_Next: def __init__(self): self.index = -1 def __next__(self): self.index += 1 return self.index o_next = O_Next() import collections print(isinstance(o_next, collections.Iterable)) print(isinstance(o_next, collections.Iterator)) >>> False >>> False
通過Python 的型別檢查,可以看到,創造器即沒有可迭代性,又不是python認可的迭代器,是一個無名英雄,還好我給它起了個名字來識別,創造器。
用標準函式 next(), 每呼叫一次創造器物件,將執行一次物件內部的__next__()方法;外部呼叫程式可決定需要呼叫多少次。
result = [next(o_next) for i in range(5)] print(result) result = [next(o_next) for i in range(5)] print(result) >>> [0, 1, 2, 3, 4] >>> [5, 6, 7, 8, 9]
若用 while True 進行呼叫,物件將會被無限迴圈地呼叫下去;可在物件內部增加停止迭代的條件判斷,通過觸發 StopIteration異常,讓呼叫程式捕獲後,來終止迭代處理。
class O_Next2(): def __init__(self): self.index = -1 def __next__(self): if self.index > 5 - 1: raise StopIteration else: self.index += 1 return self.index o_next2 = O_Next2() while True: try: print(next(o_next2)) except StopIteration as e: break >>> 0 >>> 1 >>> 2 >>> 3 >>> 4 >>> 5
二. 可迭代物件(Iterable)
具有可迭代性的物件是內部只需要實現了 __iter__()方法的物件,該方法必須返回一個實現了 __next__()方法的創造器物件(官方認為必須返回的是迭代器物件,其實不然),否則無法被外部程式用for, list()等方法正常呼叫。比如: list(), set(), tuple()等,我們可以看看常見容器型別物件的可迭代性,是否為迭代器,及__next__()返回的迭代器物件
import collections
print(isinstance(list(), collections.Iterable), isinstance(list(), collections.Iterator), type(iter(list())))
print(isinstance(set(), collections.Iterable), isinstance(set(), collections.Iterator), type(iter(set())))
print(isinstance(dict(), collections.Iterable), isinstance(dict(), collections.Iterator), type(iter(dict())))
print(isinstance(tuple(), collections.Iterable), isinstance(tuple(), collections.Iterator), type(iter(tuple())))
>>> True False <class 'list_iterator'>
>>> True False <class 'set_iterator'>
>>> True False <class 'dict_keyiterator'>
>>> True False <class 'tuple_iterator'>
可見,常見容器物件具有可迭代性,但不是迭代器,它們有各自專用的迭代器。
接下來,我們自定義一個可迭代的物件,用自定義的創造器(不是迭代器)。
class O_Iter():
def __iter__(self):
return O_Next2()
o_iter = O_Iter()
print(isinstance(o_iter, collections.Iterable), isinstance(o_iter, collections.Iterator), type(iter(o_iter)))
>>> True False <class '__main__.O_Next2'>
用for, list(),呼叫可迭代物件,將返回一個創造器物件例項, 然後無限迴圈呼叫該創造器物件的 __next__()方法,並自動處理 StopIteration 異常以終止迭代處理。
x = [i for i in o_iter]
print(x)
print(list(o_iter))
>>> [0, 1, 2, 3, 4, 5]
>>> [0, 1, 2, 3, 4, 5]
三. 迭代器(Iterator)
迭代器(Iterator)是內部同時實現了__iter__()方法和__next__()方法的物件。
__next__()方法實現創造器邏輯,__iter__()方法提供可迭代性,並返回物件自身,或其它創造器。
在python的世界裡,認為實現了__next__()方法的物件,基本都會同時實現__iter__()方法,返回自身,變成迭代器。因此,沒有了只實現__next__()方法的創造器的位置。
class O_Iterator():
def __init__(self):
self.index = -1
def __next__(self):
if self.index > 5 - 1:
raise StopIteration
else:
self.index += 1
return self.index
def __iter__(self):
return O_Iterator()
o_iterator = O_Iterator()
print(isinstance(o_iterator, collections.Iterable), isinstance(o_iterator, collections.Iterator), type(iter(o_iterator)))
>>> True True <class '__main__.O_Iterator'>
迭代器本身也具有可迭代性
x = [i for i in o_iterator]
print(x)
print(list(o_iterator))
>>> [0, 1, 2, 3, 4, 5]
>>> [0, 1, 2, 3, 4, 5]
四. 生成器(generator)
用更優雅簡潔的方式來同時實現__iter__(), __next__(), 並自動觸發StopIteration 異常
1. 生成器函式
生成器函式的例項呼叫,具有可迭代性,而且是迭代器
import collections
def o_generator(n):
index = 0
while index < n:
yield index
index += 1
print(isinstance(o_generator(5), collections.Iterable), isinstance(o_generator(5), collections.Iterator), type(iter(o_generator(5))))
>>> True True <class 'generator'>
x = [i for i in o_generator(5)]
print(x)
print(list(o_generator(5)))
>>> [0, 1, 2, 3, 4]
>>> [0, 1, 2, 3, 4]
2. 生成器表示式
列表生成器不加方括[ ],而是用圓括號( ),就變成了一個生成器物件(generator)
o_gen2 = (i*i for i in range(5))
print(isinstance(o_gen2, collections.Iterable), isinstance(o_gen2, collections.Iterator), type(iter(o_gen2)))
>>> True True <class 'generator'>
可迭代處理
o_gen2 = (i*i for i in range(5))
x = [i for i in o_gen2]
print(x)
print(list(o_gen2))
>>> [0, 1, 4, 9, 16]
>>> []
為什麼第2次 print()輸出的為空???
對同一個迭代器物件例項,全部遍歷完後,就結束了,不會自己從頭再來。 仔細分析觀察其他的程式碼,每次都是重新生成了一個迭代器物件例項(創造器物件例項),初始化後,從頭開始遍歷的。我們可以把生成器函式的例子改寫一下看看,結果將一樣。
def o_generator(n):
index = 0
while index < n:
yield index
index += 1
o_gen = o_generator(5)
x = [i for i in o_gen]
print(x)
print(list(o_gen))
>>> [0, 1, 2, 3, 4]
>>> []