Python:range 對象並非迭代器

簡評:迭代器(iterator)是惰性可迭代對象(lazy iterable),range 函數在 Python 3 中是一個惰性的可迭代對象,那麼 range 是否是迭代器呢?爲何。

TLNR:Python 3 中的 range 對象(Python 2 中的 xrange 對象)是 lazy 的,但 range 對象卻不是迭代器。函數

是的,這讓人很困惑spa

當談論 Python 中的迭代器(iterator)和可迭代對象(iterable)時,你極可能會聽到有人重複 range 是迭代器的誤解。我認爲這是很是嚴重誤解, 若是你認爲 range 對象是迭代器,那麼你關於「迭代器是如何運行」的心智模型還不夠清楚。從某種意義上來講,range 和迭代器都是「惰性」的,但它們是以至關不一樣的方式實現「惰性」的。3d

什麼是迭代器(iterator)orm

在 Python 中,可迭代對象就是你能夠迭代的任何東西,而迭代器就是實際迭代的東西。對象

Iter-ables are able to be iterated over. Iter-ators are the agents that perform the iteration.blog

可使用 iter 函數從任何可迭代對象中獲取迭代器:索引

v2-5c6a74aee26f6000cdf59c5cff9e6be7_hd.jpg

一旦有了迭代器,能夠用它作的惟一的事情就是得到它的下一個元素:ip

v2-457a2ef4f3b2d41e2e65e9ecffe4850f_hd.jpg

若是沒有更多的元素了, 則會拋出一個 stop iteration exception:
v2-9db33f878c7daa726b6faa9556b508fd_hd.jpg內存

全部的迭代器都是可迭代對象,意思是你能夠從一個迭代器中獲得一個迭代器,所以你能夠遍歷一個迭代器:
v2-00d858b0b6e1c45e4c738d08030b7835_hd.jpg字符串

應該指出的是迭代器是有狀態的,在循環遍歷一次迭代器後,若是嘗試再次循環,它將爲空:
v2-1e45994d0fd5026a1c44582fe8fd878d_hd.jpg

在 Python 3 中,enumerate、zip、reversed和其餘一些內置函數會返回迭代器:
v2-909b4d6c59cc33b0de601820413ae07f_hd.jpg

生成器(不管來自生成器函數仍是生成器表達式)是一種建立迭代器的簡單方法:

v2-676b42cca58d835e0ff52f3e071fcaa4_hd.jpg

我常常說迭代器是惰性的一次性可迭代對象。 「惰性」是由於他們只循環計算項目,「單次使用是由於一旦從一個迭代器中「消費」了一個元素以後,這個元素就永遠消失了。

什麼是 range

Python 3 中的 range 對象(Python 2 中的 xrange)能夠像任何其餘可迭代對象同樣循環使用:

v2-7574370bc89e9660ce3699a8646308d6_hd.jpg

由於 range 是可迭代對象,因此能夠從中獲得一個迭代器:
v2-651204338fff390d9ae2c65e00d89400_hd.jpg

但 range 對象自己不是迭代器,咱們不能在 range 對象上調用 next:
v2-424b5fd5829606435d555049dd363c1d_hd.jpg

與迭代器不一樣的是,咱們能夠遍歷一個 range 對象而不「消耗」它:
v2-c5866adb37ed828ebd7ef1bcc6c26bc5_hd.jpg

若是咱們使用迭代器完成此操做,則第二次循環時不會獲得任何元素:
v2-3bad586b4166e1b1d7fe701e0f0ae2c9_hd.jpg

宗上,與 zip, enumerate, or generator對象不一樣,range 對象不是迭代器。

那麼,究竟 range 是什麼

range 對象在某種意義上是「惰性的」,由於它不會生成建立時包含的每一個數字,相反,當咱們在循環中須要的時候,它纔將這些數字返回給咱們。

下面是一個 range 對象和一個生成器(是一種迭代器):

v2-539d05e0d493d8887575da154f6c8625_hd.jpg

不像生成器,range 對象有長度:
v2-3a027025e54cd25b58527f090dcf6923_hd.jpg

而且能夠被索引:
v2-50bba0bb62a1966957b39deb3837a789_hd.jpg

與迭代器不一樣,你能夠詢問他們是否包含某元素而不改變他們的狀態:
v2-2e2ec3cbbe0c5d31a9caa98a026959d8_hd.jpg

若是你想要一個 range 對象的描述,能夠稱它們爲懶序列,range 是序列(如列表,元組和字符串),但並不包含任何內存中的內容,而是經過計算來回答問題。
v2-f7a70f3f4e10092e47baf4c3ec32723c_hd.jpg

爲何這個區別很重要

若是我告訴你某個對象是一個迭代器,你會知道當在這個對象上調用 iter 函數時,總會獲得相同的的對象(按照定義):
v2-3a90773c3f5d2a7dd2a0028cc852e248_hd.jpg

確信能夠在這個對象上調用 next 函數,由於能夠在全部的迭代器上調用 next 函數:
v2-f138954eebdd1381d44389640a9ede26_hd.jpg

並且你會知道,當遍歷它時,這些元素將從迭代器中被消耗掉,有時候這個特性能夠派上用場(以特殊的方式處理迭代器):
v2-5af6ee56305dbdc431752829e5e4a44e_hd.jpg

因此雖然看起來「惰性可迭代對象」和「迭代器」之間的區別很微妙,但這些術語確實意味着不一樣的東西。 雖然「惰性可迭代對象」是一個沒有具體含義的很是廣泛的術語,但「迭代器」這個詞意味着一個具備很是特定行爲的對象。

總結

若是你知道你能夠循環遍歷某個對象,這是一個可迭代對象(iterable)。

若是你知道你正在循環遍歷的對象是在循環的時候計算出來,那麼這是一個惰性可迭代對象(lazy iterable)。

若是你知道你能夠傳遞一些東西給 next 函數,它就是一個迭代器(這是最多見的惰性可迭代對象)。

若是你能夠循環屢次而不用「耗盡」它,它不是一個迭代器。若是你不能將某些東西傳遞給 next 函數,那麼它不是一個迭代器。 Python 3 的 range 對象不是迭代器。 若是你正在指導別人關於 range 對象的知識,請不要使用「迭代器」一詞,這會讓人十分困惑,並可能致使他人開始濫用「迭代器」這個詞。

原文:Python: range is not an iterator!

相關文章
相關標籤/搜索