先說迭代器,對於string
、list
、dict
、tuple
等這類容器對象,使用for
循環遍歷是很方便的。在後臺for
語句對容器對象調用iter()
函數,iter()
是python的內置函數。iter()
會返回一個定義了next()
方法的迭代器對象,它在容器中逐個訪問容器內元素,next()
也是python的內置函數。在沒有後續元素時,next()
會拋出一個StopIteration
異常,通知for
語句循環結束。好比:python
>>> s = 'abc' >>> it = iter(s) >>> it <str_iterator object at 0x7f71fefe9d68> >>> next(it) 'a' >>> next(it) 'b' >>> next(it) 'c' >>> next(it) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
上面說的都是python自帶的容器對象,它們都實現了相應的迭代器方法,那若是是自定義類須要遍歷怎麼辦?方法很簡單,對這個類AClass,實現一個__iter__(self)
方法,使其返回一個帶有__next__(self)
方法的對象就能夠了。若是你在AClass恰好也定義了__next__(self)
方法(通常使用迭代器都會定義),那在__iter__
裏只要返回self
就能夠。廢話少說,先上代碼:函數
class Fib(object): def __init__(self, max): super(Fib, self).__init__() self.max = max def __iter__(self): self.a = 0 self.b = 1 return self def __next__(self): fib = self.a if fib > self.max: raise StopIteration self.a, self.b = self.b, self.a + self.b return fib def main(): fib = Fib(100) for i in fib: print(i) if __name__ == '__main__': main()
簡單講下代碼會幹什麼,定義了一個Fib類,用於生成fibonacci序列。用for遍歷時會逐個打印生成的fibonacci數,max是生成的fibonacci序列中數字大小的上限。工具
在類的實現中,定義了一個__iter__(self)
方法,這個方法是在遍歷時被iter()
調用,返回一個迭代器。由於在遍歷的時候,是直接調用的python內置函數iter()
,由iter()
經過調用__iter__(self)
得到對象的迭代器。有了迭代器,就能夠逐個遍歷元素了。而逐個遍歷的時候,也是使用內置的next()
函數經過調用對象的__next__(self)
方法對迭代器對象進行遍歷。因此要實現__iter__(self)
和__next__(self)
。並且由於實現了__next__(self)
,因此在實現__iter__(self)
的時候,直接返回self就能夠。code
爲了更好理解,我再簡單重複下上面說的那一段:在循環遍歷自定義容器對象時,會使用python內置函數iter()
調用遍歷對象的__iter__(self)
得到一個迭代器,以後再循環對這個迭代器使用next()
調用迭代器對象的__next__(self)
。__iter__
只會被調用一次,而__next__
會被調用 n 次。對象
下面說生成器。內存
生成器(Generator)是建立迭代器的簡單而強大的工具。它們寫起來就像是正規的函數,只是在須要返回數據的時候使用
yield
語句。每次next()
被調用時,生成器會返回它脫離的位置(它記憶語句最後一次執行的位置和全部的數據值)。如下示例演示了生成器能夠很簡單的建立出來:ci
>>> def reverse(data): ... for index in range(len(data)-1, -1, -1): ... yield data[index] ... >>> for char in reverse('hello'): ... print(char) ... o l l e h
關於迭代器和生成器的區別,生成器能作到迭代器能作的全部事,並且由於自動建立了__iter__()
和 next()
方法,生成器顯得特別簡潔,並且生成器也是高效的。除了建立和保存程序狀態的自動方法,當發生器終結時,還會自動拋出StopIteration
異常。一個帶有yield
的函數就是一個 生成器,它和普通函數不一樣,生成一個 generator 看起來像函數調用,但不會執行任何函數代碼,直到對其調用next()
(在 for 循環中會自動調用next()
)纔開始執行。雖然執行流程仍按函數的流程執行,但每執行到一個yield
語句就會中斷,並返回一個迭代值,下次執行時從yield
的下一個語句繼續執行。看起來就好像一個函數在正常執行的過程當中被yield
中斷了數次,每次中斷都會經過yield
返回當前的迭代值(yield
暫停一個函數,next()
從其暫停處恢復其運行)。generator
另外對於生成器,python還提供了一個生成器表達式:相似與一個yield
值的匿名函數。表達式自己看起來像列表推到, 但不是用方括號而是用圓括號包圍起來:string
>>> unique_characters = {'E', 'D', 'M', 'O', 'N', 'S', 'R', 'Y'} >>> gen = (ord(c) for c in unique_characters) >>> gen <generator object <genexpr> at 0x7f2be4668678> >>> for i in gen: ... print(i) ... 69 79 83 77 82 78 89 68 >>>
若是須要,能夠將生成器表達式傳給tuple
、list
或是set
來迭代全部的值而且返回元組、列表或是集合。在這種狀況下,不須要一對額外的括號 ———— 直接將生成器表達式 ord(c) for c in unique_characters
傳給tuple()
等函數就能夠了, Python 會推斷出它是一個生成器表達式。it
最後,爲何要使用生成器?由於效率。使用生成器表達式取代列表解析能夠同時節省 cpu 和 內存(ram)。若是你構造一個列表的目的僅僅是傳遞給別的函數,(好比 傳遞給tuple()
或者set())
, 那就用生成器表達式替代吧!