全部的生成器都是迭代器python
關於迭代器和生成器的一種定義:迭代器用於從集合中取出元素;生成器用於憑空生成元素。函數
Python中,全部的集合都是能夠迭代的,在Python語言內部,迭代器用於支持:測試
如同標題本文的標題同樣,這邊文章主要講解三個方面,可迭代對象,迭代器,生成器,下面逐個開始理解spa
先經過下面單詞序列例子來理解:code
1 import re 2 import reprlib 3 4 5 RE_WORD = re.compile('\w+') 6 7 8 class Sentence(object): 9 def __init__(self,text): 10 self.text = text 11 self.words = RE_WORD.findall(text) 12 13 def __getitem__(self, index): 14 return self.words[index] 15 16 def __len__(self): 17 return len(self.words) 18 19 def __repr__(self): 20 """ 21 用於打印實例化對象時,顯示自定義內容, 22 reprlib.repr函數生成的字符換最多有30個字符,當超過怎會經過省略號顯示 23 :return: 自定義內容格式 24 """ 25 return 'Sentence(%s)' % reprlib.repr(self.text) 26 27 s = Sentence('"the time has come," the Walrus said,') 28 print(s) 29 print(type(s)) 30 for word in s: 31 print(word) 32 33 print(list(s))
上面代碼的運行結果:對象
首先從結果來看,咱們能夠看出這個類的實例是能夠迭代的,
而且咱們從打印print(s)的結果能夠看出,顯示的也是咱們定義的內容,若是咱們在類中沒有經過__repr__自定義,打印結果將爲:
<__main__.Sentence object at 0x102a08fd0>
同時這裏的實例化對象也是一個序列,因此咱們能夠經過s[0]這種方式來獲取每一個元素
咱們都知道序列能夠迭代,那麼序列爲啥能夠迭代,繼續深刻理解blog
解釋器須要迭代對象x時,會自動調用iter(x)索引
內置的iter函數做用:接口
任何python序列能夠迭代的緣由是,他們都實現了__getitem__方法,而且標準的序列也實現了__iter__方法。字符串
關於如何判斷x對象是否爲可迭代對象,有兩種方法:iter(x)或者isinstance(x,abc.Iterable)
那麼這兩種判斷法有什麼區別麼?
其實從Python3.4以後建議是經過iter(x)方法來進行判斷,由於iter方法會考慮__getitem__方法,而abc.Iterable不會考慮,因此iter(x)的判斷方法更加準確
就像我最開始寫的那個例子,分別經過這兩種方式來測試,能夠看出,其實這個類是能夠迭代的,可是經過abc.Iterable的方式來判斷,確實不可迭代的
關於可迭代對象的一個小結:
首先咱們要明白可迭代的對象和迭代器之間的關係:
Python從可迭代的對象中獲取迭代器
一個簡單的例子,當咱們循環字符串的時候,字符串就是一個可迭代的對象,背後就是有迭代器,只不過咱們看不到,下面爲代碼例子:
1 # 經過for循環方式 2 s = "ABC" 3 for i in s: 4 print(i) 5 6 7 print(''.center(50, '-')) 8 9 # 經過while循環方式 10 it = iter(s) 11 12 while True: 13 try: 14 print(next(it)) 15 except StopIteration: 16 del it 17 break
這兩種方式均可以獲取可迭代對象裏的內容,可是while循環的方式若是不經過try/except方式獲取異常,最後就會提示StopIteration的錯誤,這是由於Python語言內部會處理for循環和其餘迭代上下文(如列表推導,元組拆包等等)中的StopIteration
標準的迭代器接口有兩個方法:
由於迭代器只須要__next__和__iter__兩個方法,因此除了調用next()方法,以及捕獲StopIteration異常以外,沒有辦法檢查是否還有遺留元素,而且沒有辦法還原迭代器,若是想要再次迭代,就須要調用iter(...)傳入以前構建迭代器的可迭代對象
咱們把剛開始寫的sentence類經過迭代器的方式來實現,要說的是這種寫法不符合python的習慣作法,這裏是爲了更好的理解迭代器和可迭代對象之間的重要區別
1 import re 2 import reprlib 3 from collections import abc 4 5 6 RE_WORD = re.compile('\w+') 7 8 9 class Sentence: 10 11 def __init__(self,text): 12 self.text = text 13 self.words = RE_WORD.findall(text) 14 15 def __repr__(self): 16 return "Sentence(%s)" % reprlib.repr(self.text) 17 18 def __iter__(self): 19 return SentenceIterator(self.words) 20 21 22 class SentenceIterator: 23 24 def __init__(self,words): 25 self.words = words 26 self.index = 0 27 28 def __next__(self): 29 try: 30 word = self.words[self.index] 31 except IndexError: 32 raise StopIteration() 33 self.index += 1 34 return word 35 36 def __iter__(self): 37 return self
這樣咱們就能夠很清楚的明白,咱們定義了一個SenteneIterator是一個迭代器,也實現了迭代器應該有的兩種方法:__next__和__iter__方法,這樣咱們經過 issubclass(SentenceIterator,abc.Iterator)檢查
這裏咱們還能看到可迭代對象和迭代器的區別:
可迭代對象有__iter__方法,每次都實例化一個新的迭代器
迭代器要實現__next__和__iter__兩個方法,__next__用於獲取下一個元素,__iter__方法用於迭代器自己,所以迭代器能夠迭代,可是可迭代對象不是迭代器
有人確定在想在Sentence類中實現__next__方法,讓Sentence類既是可迭代對象也是自身的迭代器,可是這種想法是不對的,這是也是常見的反模式。因此可迭代對象必定不能是自身的迭代器
先經過用生成器方式替換上個例子中SentenceIterator類,例子以下:
1 import re 2 import reprlib 3 4 5 RE_WORD = re.compile('\w+') 6 7 8 class Sentence: 9 10 def __init__(self,text): 11 self.text = text 12 self.words = RE_WORD.findall(text) 13 14 def __repr__(self): 15 return 'Sentence(%s)' % reprlib.repr(self.text) 16 17 def __iter__(self): 18 for word in self.words: 19 yield word
在上面這個代碼中,咱們經過yield關鍵字,這裏的__iter__函數其實就是生成器函數,迭代器實際上是生成器對象,每次調用__iter__方法,都會自動建立。
Python函數定義體中有yield關鍵字,該函數就是生成器函數。
生成器函數會建立一個生成器對象,包裝生成器函數的定義體,把生成器傳給next(...)函數時,生成器函數會向前,執行函數定義體中的下一個yield語句,返回產出的值,並在函數定義體的當前位置暫停,最終,函數的定義體返回時,外層的生成器對象會拋出SotpIteration異常,這一點和迭代器協議一致。
下面是一個生成器的例子:
這裏其實咱們要明白進行for循環的過程其實就是在隱式的調用next()函數
當咱們寫了好幾種Sentence類的時候,感受咱們經過生成器方式實現的挺簡單了,其實還有更簡單的方法的,代碼例子以下,這裏的finditer函數構建了一個迭代器:
1 import re 2 import reprlib 3 4 5 RE_WORD = re.compile('\w+') 6 7 8 9 class Sentence: 10 11 def __init__(self,text): 12 self.text = text 13 14 def __repr__(self): 15 return 'Sentence(%s)' % reprlib.repr(self.text) 16 17 def __iter__(self): 18 for match in RE_WORD.finditer(self.text): 19 yield match.group()
生成器表達式能夠理解爲列表推導的惰性版本,不會直接構成列表,而是返回一個生成器,按需惰性生成元素。
關於實現Sentence,還能夠經過生成器表達式。
1 import re 2 import reprlib 3 4 5 RE_WORD = re.compile('\w+') 6 7 8 class Sentence: 9 10 def __init__(self,text): 11 self.text = text 12 13 def __repr__(self): 14 return 'Sentence(%s)' % reprlib.repr(self.text) 15 16 def __iter__(self): 17 return (match.group() for match in RE_WORD.finditer(self.text))