目錄
1 可迭代對象
1.1什麼是可迭代對象、如何判斷是否是可迭代對象
在Python中,我們把所有可以迭代的對象統稱爲可迭代對象,有一個類專門與之對應:Iterable。大多數容器也是可迭代的。但還有很多東西是可迭代的。例如打開的文件、打開的套接字等等。在容器通常是有限的情況下,可迭代可以表示無限的數據流。
可迭代對象是可以返回迭代器(目的是返回其所有元素)的任何對象,不一定是數據結構。這聽起來有點尷尬,但是可迭代對象和迭代器之間存在重要區別。
>>> from collections import Iterable
>>> isinstance(123, Iterable)
False
>>> isinstance(True, Iterable)
False
>>> isinstance('abc', Iterable)
True
>>> isinstance([], Iterable)
True
>>> isinstance({}, Iterable)
True
>>> isinstance((), Iterable)
True
怎麼讓一個對象可迭代呢?畢竟,很多時候,我們需要用到的對象不止Python內置的這些數據類型,還有自定義的數據類型。答案就是實現__iter__()方法,只要一個對象定義了__iter__()方法,那麼它就是可迭代對象。
from collections.abc import Iterable
class A():
def __iter__(self):
pass
print('A()是可迭代對象嗎:',isinstance(A(),Iterable))
//結果輸出爲:
//A()是可迭代對象嗎: True
只要一個對象定義了__iter__()方法,那麼它就是可迭代對象。
2 迭代器
迭代器是對可迭代對象的改造升級,上面說過,一個對象定義了__iter__()方法,那麼它就是可迭代對象,進一步地,如果一個對象同時實現了__iter__()和__next__()方法,那麼它就是迭代器。
在Python中,也有一個類與迭代器對應:Iterator。所以,要判斷一個類是否是迭代器,只要判斷是否是Iterator類的實例即可。
from collections.abc import Iterable
from collections.abc import Iterator
class B():
def __iter__(self):
pass
def __next__(self):
pass
print('B()是可迭代對象嗎:',isinstance(B(), Iterable))
print('B()是迭代器嗎:',isinstance(B(), Iterator))
/*
結果輸出如下:
B()是可迭代對象嗎: True
B()是迭代器嗎: True
*/
3 for循環的本質
x = [1, 2, 3]
x = iter(x)
try:
while True:
print(next(x))
except StopIteration:
pass
從上我們可以看出,for循環的本質是先將可迭代對象列表x利用iter(x)調用__iter__()方法,返回迭代器。然後通過next(x)可以調用魔法函數__next__(),實現下一個元素的獲取。
4 生成器
終於,我們到達了目的地!生成器是我最喜歡的Python語言特性。生成器是一種特殊的迭代器,優雅的迭代器。
生成器使您可以像上面的Fibonacci序列迭代器示例一樣編寫迭代器,但是使用簡潔的語法,避免使用__iter __()和__next __()方法編寫類。
def fib(max):
n, a, b = 0, 0, 1
while n < max:
print(b)
a, b = b, a + b
n = n + 1
return 'done'
>>> fib(6)
1
1
2
3
5
8
'done'
仔細觀察,可以看出,fib
函數實際上是定義了斐波拉契數列的推算規則,可以從第一個元素開始,推算出後續任意的元素,這種邏輯其實非常類似generator。也就是說,上面的函數和generator僅一步之遙。要把fib
函數變成generator,只需要把print(b)
改爲yield b
就可以了:
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
return 'done'
這裏,最難理解的就是generator和函數的執行流程不一樣。函數是順序執行,遇到return
語句或者最後一行函數語句就返回。而變成generator的函數,在每次調用next()
的時候執行,遇到yield
語句返回,再次執行時從上次返回的yield
語句處繼續執行。