記得在剛開始學Python的時候,看到可迭代對象(iterable)、迭代器(iterator)和生成器(generator)這三個名詞時,完全懵逼了,根本就不知道是啥意識。現在以自己的理解來詳解下這三者的關系。
一、可迭代對象(iterable)
我們知道,在Python世界里,一切皆對象。對象根據定義的維度,又可以分為各種不同的類型,比如:文件對象,字符串對象,列表對象。。。等等。
那什么對象才能叫做可迭代對象呢?一句話:“實現了__inter__方法的對象就叫做可迭代對象”,__inter__方法的作用就是返回一個迭代器對象。直觀理解就是能用for循環進行迭代的對象就是可迭代對象。比如:字符串,列表,元祖,字典,集合等等,都是可迭代對象。
for循環與__inter()__方法又有什么關系呢?
比如我們在對一個列表進行迭代時,如下代碼:
x = [1,2,3] for i in x: print(i)
實際執行情況如下圖:
- 調用可迭代對象的__inter__方法返回一個迭代器對象(iterator)
- 不斷調用迭代器的__next__方法返回元素
- 知道迭代完成后,處理StopIteration異常
二、迭代器(iterator)
那么什么叫迭代器呢?它是一個帶狀態的對象,他能在你調用next()
方法的時候返回容器中的下一個值,任何實現了__iter__
和__next__()
方法的對象都是迭代器,__iter__
返回迭代器自身,__next__
返回容器中的下一個值,如果容器中沒有更多元素了,則拋出StopIteration異常。
根據定義,我們可以寫一個迭代器,並通過next()方法來調用,如下代碼:
class Fib(): def __init__(self,max): self.n = 0 self.prev = 0 self.curr = 1 self.max = max def __iter__(self): return self def __next__(self): if self.n < self.max: value = self.curr self.curr += self.prev self.prev = value self.n += 1 return value else: raise StopIteration # 調用 f = Fib(5) print(next(f)) print(next(f)) print(next(f)) print(next(f)) print(next(f)) print(next(f))
執行結果:
"C:\Program Files\Python36\python.exe" D:/Git/Test_Framework/utils/1.py 1 1 2 3 5 Traceback (most recent call last): File "D:/Git/Test_Framework/utils/1.py", line 37, in <module> print(next(f)) File "D:/Git/Test_Framework/utils/1.py", line 29, in __next__ raise StopIteration StopIteration Process finished with exit code 1
迭代器就像一個懶加載的工廠,等到有人需要的時候才給它生成值返回,沒調用的時候就處於休眠狀態等待下一次調用。直到無元素可調用,返回StopIteration異常。
三、生成器(generator)
生成器其實是一種特殊的迭代器,不過這種迭代器更加優雅。它不需要再像上面的類一樣寫__iter__()
和__next__()
方法了,只需要一個yiled
關鍵字。 生成器一定是迭代器(反之不成立),因此任何生成器也是以一種懶加載的模式生成值。用生成器來實現斐波那契數列的例子是:
def fib(max): n, prev, curr = 0, 0, 1 while n<max: yield curr prev, curr = curr, curr + prev n += 1
生成器特殊的地方在於函數體中沒有return
關鍵字,函數的返回值是一個生成器對象。當執行f=fib()
返回的是一個生成器對象,此時函數體中的代碼並不會執行,只有顯示或隱示地調用next的時候才會真正執行里面的代碼。
生成器還有一個send方法,可以往生成器里的變量傳值,如下代碼:
def foo(): print("first") count=yield print(count) yield f = foo() f.send(None) f.send(2)
調用過程:
-
f = foo()返回一個生成器
-
f.send(None)進入函數執行代碼,遇到count=yield,凍結並跳出函數體
-
f.send(2)再次進入函數體,接着凍結的代碼繼續執行,把2傳給變量count,打印count,遇到yield凍結並跳出函數
四、生成器表達式(generator expression)
生成器表達式是列表推倒式的生成器版本,看起來像列表推導式,但是它返回的是一個生成器對象而不是列表對象。
a = (x for x in range(10)) print(a)
執行結果:
"C:\Program Files\Python36\python.exe" D:/Git/Test_Framework/utils/1.py <generator object <genexpr> at 0x000000000289D8E0> Process finished with exit code 0