一文解開可迭代對象和迭代器的神秘面紗

2021-03-02 日常學python

可迭代對象和迭代器是兩種不同的數據類型,它們都在我們的編程中時常可以遇到。當然他們之間也有很大的關聯,接下來就讓我們把它們搞定。

1.迭代器(Iterator)

迭代器表示的是一個數據流,並不表示一個數據實體,我們可以使用next()方法計算下一個數據。或者說,可以使用next()方法的就是迭代器。

生成器是迭代器,生成器可以使用下面這種方式生成。下面請看具體實例

a = (i for i in range(10))
print(next(a))  #output:0
print(next(a))  #output:1
print(a[4])    
# error:TypeError: 'generator' object is not subscriptable

如代碼所示,使用next()方法可以取出生成器的值,但是使用類似於list的切片便會報錯,所以說,生成器不是一個數據實體,而是一個數據流,只能按順序取出數據。

那如何用代碼判斷這到底是不是迭代器呢?我們可以使用isinstance關鍵字

import collections
a = (i for i in range(10))
print(type(a))
print(isinstance(a,collections.Iterator))


<class 'generator'>
True

可以看到生成器確實是迭代器。

生成器函數也是迭代器,即使用yield定義的類函數體,下面可以驗證一下。

import collections
def fib(max):
   n, a, b = 0, 0, 1
   while n < max:
       yield b
       a, b = b, a + b
       n = n + 1
   return 'done'
f = fib(8)
print(type(f))
print(isinstance(f,collections.Iterator))


<class 'generator'>
True

2.可迭代對象(Iterable)

可迭代對象指的就是可以迭代的對象,或者說可以作用於for循環的都是可迭代對象。那這就包括list,tuplt,dict,set等類型,也包括上面講的生成器。是的,它們都可以用於for循環。

a = [1,2,3,4,5]
for x in a:
   print(x)

這對list可以順利執行,同樣,對於其他序列也是同樣正確的。

那接下來我們判斷一下到底list,set,dict,tuple是不是可迭代類型。判斷是不是可迭代類型可以使用isinstance和Iterable對象。

import collections
a = [1,2,3]
print(type(a))
print(isinstance(a,collections.Iterable))

# output:
<class 'list'>
True

可以看到,list確實是可迭代類型。

3.兩者之間的關係

事實上,Iterator是Iterable的子類型,Iterator是從Iterable繼承下來的。

查看源碼可以看到這一點,值得注意的是,next()方法內部正是用__next__()實現的。


class Iterator(Iterable):

   __slots__ = ()

   @abstractmethod
   def __next__(self):
       'Return the next item from the iterator. When exhausted, raise StopIteration'
       raise StopIteration

   def __iter__(self):
       return self

因此,兩者之間關係已經很明了了,迭代器肯定是可迭代對象,但可迭代對象不一定是迭代器。

 4.動手實現一個迭代器

我們自己動手寫一個有助於我們從原理層面理解迭代器,下面我便帶著大家看看迭代器如何實現。

從前面我們展示出來的迭代器的部分源碼,我們可以看出來,迭代器首先是繼承了Iterable,其次更重要的是迭代器實現了兩個虛擬方法,一個是__next__(self),另一個是__iter__(self)方法,其中__iter__(self)是從Iterable中繼承下來的。因此實現一個屬於自己的迭代器的關鍵是要實現這兩個方法。

class Fib:
   def __init__(self):
       self.prev = 0
       self.curr = 1

   def __iter__(self):
       return self

   def __next__(self):
       value = self.curr
       self.curr += 1
       self.prev = value
       return value

上面這段代碼實現了一個簡單的從1開始的遞增序列的一個迭代器,其中__iter__(self)返回迭代器本身,而__next__(self)實現了如何去取得下一個值。

那麼接下來我們來取出下一個值,驗證一下我們的迭代器正不正確。

f = Fib()
print(next(f))
print(next(f))
print(f.__next__())
print(f.__next__())

# output:
1
2
3
4

結果正是我們當初設計的迭代器一樣。這裡使用了next()函數,也用了__next__()方法,其實本質是一樣的。只不過next()包裝了__next__()方法,是一個全局的函數,而__next__()是一個對象的方法,只能由對象調用。

這樣的方法在Python中還有許多,例如我們常用的len()函數,就是這樣的設計原理,在對象內部有一個__len__()方法,調用len(),內部實現的就是__len__().

s = 'abcdefg'
print(s.__len__())  #output:7
print(len(s))       #output:7

如代碼所示,有兩種方法可以求取長度。我們在實際寫代碼中通常直接使用len()方法,因為這個調用起來更簡單,可讀性也更加好,這也是為什麼Python要提供len()方法的原因,畢竟Python的設計是遵循優雅簡潔的原則進行的。

注意:在運行上面代碼時,不要將迭代器對象轉化為list或者set等數據結構,也就是不要執行list(f),這會使得這個代表著無限序列的迭代器一次性裝入內存。我因為不小心執行了這個代碼,導致內存使用率到達90%多,最後重啟電腦才恢復。

最後

那為什麼需要迭代器呢?那是因為我們的內存是有限的,但可能需要表示的數據是無限的,那這個時候我們可以使用迭代器,在Python3.x中,以前所有返回序列的方法,都已經變為返回迭代器了。我們可以將迭代器看作一個懶惰的傢伙,他只有會在要使用時才會給你計算和提供一個數據,這可以讓我們更省內存,運行效率更高。

推薦閱讀:

Ajax網頁爬取案例詳解

用python來分析一波股票

日常學python

代碼不止bug,還有美和樂趣

相關焦點

  • python中常見的對象辨析(可迭代對象、迭代器對象、生成器對象)
    一 可迭代對象IterableA.定義可作用於for循環的對象叫可迭代對象B.包括1.集合數據類型 序列,字典,集合2.迭代器對象C.訪問可迭代對象1.集合數據類型使用各自的訪問方法2.迭代器對象使用迭代器對象訪問的方法D.注意1.具有
  • Python中可迭代對象、迭代器以及iter函數的兩個用法詳解
    第一個是可迭代對象(Iterable),第二個是迭代器(Iterator),第三個是生成器(Generator),這裡暫且不談生成器。 可迭代對象 列表、元組、字符串、字典等都是可迭代對象,可以使用for循環遍歷出所有元素的都可以稱為可迭代對象(Iterable)。
  • Python進階:迭代器與迭代器切片
    1、迭代與迭代器首先,有幾個基本概念要澄清:迭代、可迭代對象、迭代器。迭代 是一種遍歷容器類型對象(例如字符串、列表、字典等等)的方式,例如,我們說迭代一個字符串「abc」,指的就是從左往右依次地、逐個地取出它的全部字符的過程。
  • Python 迭代器和 C++ 迭代器,最大的不同竟然是……
    當我初學 Python 的時候,我將迭代器理解為一種能夠放在「for xxx in …」的「…」位置的東西;後來隨著學習的深入,我了解到迭代器就是一種實現了迭代器協議的對象;學習 C++ 時,我了解到迭代器是一種行為和指針類似的對象…事實上,迭代器是一個伴隨著迭代器模式(Iterator Pattern)而生的抽象概念,其目的是分離並統一不同的數據結構訪問其中數據的方式,從而使得各種需要訪問數據結構的函數
  • Python迭代器和生成器,三個練習你絕對能搞定
    1、 迭代器1.1 容器首先,在了解迭代器之前,需要知道什麼是容器一切都是對象,對象的抽象就是類,而對象的集合就是容器。容器,就是有多個對象組成的東西。比如:列表[0,1,2],元組(1,2,3),字典{』0:'0','1':"1'} 集合{1,2,3}都是容器所有的容器都是可迭代對象,也就是可以遍曆元素。
  • 雲計算開發學習筆記:Python3迭代器與生成器
    迭代器迭代是Python最強大的功能之一,是訪問集合元素的一種方式。迭代器是一個可以記住遍歷的位置的對象。迭代器對象從集合的第一個元素開始訪問,直到所有的元素被訪問完結束。迭代器只能往前不會後退。
  • Python中的迭代器和生成器,模塊和第三方模塊都是些什麼?
    Python我們已經學了很多東西了,今天我們來聊一聊Python中什麼是迭代器和生成器,什麼又是模塊,什麼是第三方模塊。廢話不多說,我們開始吧!苦短人生,我用Python1.迭代器迭代器對象要求支持迭代器協議的對象,在Python中,支持迭代器協議就是實現對象的__iter__()和__next__()方法。
  • 設計模式之迭代器模式(Java實現)
    設計模式之迭代器模式(Java實現)迭代器模式(Iterator Pattern) :它提供一種方式訪問一個容器對象的各個元素,而又不需暴露該對象的內部細節迭代器模式是為了解決遍歷容器中的元素而產生的。先看看下面的通用類圖:
  • Python——迭代器的幾個高級用法
    dropwhile是itemtools當中的一個函數,它可以 接收一個我們自定義的過濾函數和迭代器重新生成一個新的迭代器 ,這個新的迭代器當中會過濾掉之前迭代器頭部不符合我們要求的數據:在剛才的例子當中我們想要過濾掉頭部加了#注釋的部分,我們可以這麼操作:from itertools
  • 帶你一文讀懂ES6的Symbol
    Symbol.iterator從名字上就能看出,跟我們經常接觸到的迭代器有很大的關係。在講Symbol.iterator之前,我們先來回顧下迭代器的概念。什麼是迭代器?迭代器就是能讓你遍歷並操作一個集合中的每一個元素的方法。在Javascript中,每一次循環都可以被稱為迭代。你可以用for循環來遍歷一個數據或者對象。
  • 揭開古埃及的神秘面紗
    世界四大文明古國分別為:古巴比倫,古埃及,古印度和古代中國,這四大文明古國也代表著不同的文明流域。說起這婦孺皆知的文明古國,我們不免被古埃及的神秘所深深吸引,情難自禁的想要揭開這神秘面紗。人們對於神秘古埃及的好奇無外乎是關於古埃及的文化建築,宗教習俗與獨特的科學。一、再現象形文字、金字塔絢麗多彩的象形文字自出現以後一直為古埃及人所使用,這一文字在古埃及人的手裡煥發出無限生機。此外,埃及人還使用另兩種文字,即世俗文和僧侶文,值得一提的是,這兩種文字也都是來源於象形文字,只是在書寫方式上有所不同。
  • 《前端5分鐘》之迭代器模式的N+1種應用場景
    >實現一個對象迭代器實現路徑查找/賦值迭代器如何用迭代器的思想解決分支循環嵌套問題實現一個圖片播放器正文1.迭代器的含義迭代器模式主要的思想就是在不暴露對象內部結構的同時可以按照一定順序訪問對象內部的元素。
  • 古埃及採石場的神秘面紗
    古埃及是世界聞名古國留下的有生產物,在文明的標尺上,埃及是毋容置疑的古國之首,在世界的坐標中,埃及是地球上當之無愧的「帝國」,在影響力上,古埃及滅亡1700餘年,仍然是探險家和考古家的熱衷對象,不論是自身的文化還是建築風格影響著整個世界,古埃及神秘的地區有著讓人嚮往而又痴迷的故事和文化
  • 可現學現用的10個Python技巧
    使用列表推導列表推導用於從其他可迭代對象(iterable)創建新列表。列表推導返回列表時,它們由包含表達式的方括號組成,該表達式針對每個元素連同for loop加以執行,以便對每個元素執行迭代處理。列表推導速度更快,因為它針對Python解釋器進行了優化,以便循環期間發現可預測的模式。
  • 重學Java 設計模式:實戰迭代器模式「模擬公司組織架構樹結構關係...
    目錄一、前言二、開發環境三、迭代器模式介紹四、案例場景模擬五、迭代器模式遍歷組織結構Iterable,提供獲取迭代器,這個接口類會被Collection繼承。Iterator,提供了兩個方法的定義;hasNext、next,會在具體的數據結構中寫實現方式。除了這樣通用的迭代器實現方式外,我們的組織關係結構樹,是由節點和節點間的關係鏈構成,所以會比上述的內容多一些入參。
  • Java中list的刪除為什麼要用迭代器?
    Java中對list集合的刪除需要用到迭代器一、增強for循環刪除這種方式刪除會報錯。那麼這個異常是什麼導致的呢。首先我們要明白一點,增強for循環,在編譯過後遍歷是通過迭代器來實現,也正是這個實現方式,導致出現了問題。問題的分析:上面是list實現迭代器的源碼,可以看到有兩值。一個是expectedModCount,還一個是modCount,這兩個值就是報錯的關鍵。