for 循環 是我們在 Python 裏非常常用的一個語法,但你有沒有思考過 for 循環是怎樣實現的?
如果你以前接觸過 C++,應該會知道類似 for (int i = 0; i < 100; i++)
這樣的寫法,它定義了 循環的執行條件 i < 100
以及 每次循環結束後執行的語句 i++
,而 for
本身只起到讓代碼重複執行的作用,並沒有什麼額外的功能。這在 Python 中其實更像是 while 循環:
i = 0
while i < 100:
# 執行循環代碼
i += 1
但 Python 裏的 for 循環卻不一樣。使用 for
時,我們沒有額外指定結束條件,也不需要一個用來計數的數值,甚至可以通過一個字符串進行循環。之所以可以這樣,是因爲 Python 中的 迭代器(Iterator) 以及 可迭代對象(Iterable) 。
如果一個對象定義了 __iter__
和 __next__
兩個方法, 它就是一個迭代器 。對於迭代器來說,__iter__
返回的是它自身 self ,__next__
則是 返回迭代器中的下一個值 ,如果沒有值了則拋出一個 StopIteration
的異常。關於這點,你可以想象成一個只進不退的標記位,每次調用 __next__
,就會將標記往後移一個元素並返回,直到結束。
有了迭代器的概念之後,如果一個對象定義了 __iter__
和方法,返回一個迭代器對象,那麼 它就是一個可迭代的對象 。
從表現上來說,一個對象 可迭代 ,那麼它就可以被 for 循環使用。比如我們經常用到的 list、dict、str 等類型,都是可迭代的,所以也就可以通過 for 循環進行遍歷,或者更準確的說:被迭代。
有一點繞,我們再來理一理迭代器(Iterator)和可迭代(Iterable)這兩個的差別:
- 一個 迭代器一定是可迭代對象 ,因爲它一定有
__iter__
方法。反過來則不成立。(事實上,Iterator 就是 Iterable 的子類) - 迭代器的
__iter__
方法 返回的是自身,並不產生新實例 。而可迭代對象的__iter__
方法通常會生成一個新的迭代器對象。
__iter__
、__next__
分別對應於 Python 的內置函數 iter()
和 next()
:比如 iter(aList)
就相當於 aList.__iter__()
。
所以關於上述兩點,我們可以有以下的例子來驗證:
迭代器和可迭代之間的繼承關係。
__iter__
方法返回值的區別。id 相同代表是同一個實例。
明白了上述的概念之後,for 循環的實現就好理解了:
- 首先 for 循環會調用可迭代對象的
__iter__
方法,獲取相應的迭代器 - 每次循環,將迭代器的
__next__
方法的返回值賦值給循環變量 - 直到捕獲迭代器拋出的
StopIteration
異常,循環結束
再來看個例子:
思考題: 想一想爲什麼迭代器 aListIter 被 for 循環迭代第二次的時候就沒有輸出了?
既然 __next__
方法可以自己定義,我們也可以自己實現一個迭代器。比如要 輸出一個斐波那契數列 (每一位數值都是前兩位數值之和,原題回覆關鍵字 906 ),通常的做法是循環,“高級”一點的做法是遞歸。但我們也可以直接寫一個斐波那契迭代器:
# 定義迭代器
class Fibonacci():
def __init__(self):
self.a = 0
self.b = 1
def __iter__(self):
return self
def __next__(self):
# 結束條件
if self.b > 100:
raise StopIteration
# 更新一次數值
self.a, self.b = self.b, self.a + self.b
return self.a
# 創建迭代器
fib = Fibonacci()
# 進行迭代
for f in fib:
print(f, end=' ')
輸出:
1 1 2 3 5 8 13 21 34 55 89
這個例子中,我們並沒有保存一個序列,只是定義了一種規則,就也可以被迭代。
使用迭代器的好處在於:它是一種 延遲操作 ,即當需要用到的時候纔去產生結果。比如對於一個序列來說,如果我們要遍歷它,並不需要再一開始就把所有元素都生成好,而是只需要知道每個元素的下一個元素是什麼就可以了。這樣可以節省很多空間,尤其對於數量很大的集合來說。
如果你不懂迭代器的概念,並不影響在代碼中使用 for 循環。但瞭解之後,你會對代碼理解得更透徹,同時這也是爲我們後面要講到的 生成器 做鋪墊。
════
其他文章及回答:
如何自學Python | 新手引導 | 精選Python問答 | Python單詞表 | 人工智能 | 爬蟲 | 我用Python | requests | 計算機視覺 | 字符播放器 | 一圖學Python
歡迎搜索及關注公衆號: Crossin的編程教室