Python中的可迭代對象、迭代器、For循環工做機制、生成器

1.iterable iterator區別python

要了解二者區別,先要了解一下迭代器協議:網絡

迭代器協議是指:對象須要提供__next__()方法,它返回迭代中的元素,在沒有更多元素後,拋出StopIteration異常,終止迭代。函數

可迭代對象就是:實現了迭代器協議的對象。工具

協議是一種約定,可迭代對象實現迭代器協議,Python的內置工具(如for循環,sum,min,max函數等)經過迭代器協議訪問對象,所以,for循環並不須要知道對象具體是什麼,只須要知道對象可以實現迭代器協議便可。學習

迭代器(iterator)與可迭代對象(iterable)並非同一個概念。spa

直觀上:code

1.可迭代對象(iterable):凡是具備__iter__的方法的類,都是可迭代的類。可迭代類建立的對象實現了__iter__方法,所以就是可迭代對象。用list、tuple等容器建立的對象,都是可迭代對象。可迭代對象經過__iter__方法返回一個迭代器,而後在內部調用__next__方法進行迭代,最後沒有元素時,拋出異常(這個異常python本身會處理,不會讓開發者看見)。對象

2.迭代器(iterator):迭代器對象必須同時實現__iter__和__next__方法纔是迭代器。對於迭代器來講,__iter__ 返回的是它自身 self,__next__ 則是返回迭代器中的下一個值,最後沒有元素時,拋出異常(異常能夠被開發者看到)。教程

從上面2點能夠看出:圖片

1.迭代器必定是可迭代對象,由於它實現了__iter__()方法;

2.經過iter()方法(在類的內部就是__iter__)可以使一個可迭代對象返回一個迭代器。

Python學習qun:5563,70268,這裏是python學習者彙集地,歡迎喜歡Python的小夥伴!

3.迭代器的 iter 方法返回的是自身,並不產生新的迭代器對象。而可迭代對象的 iter 方法一般會返回一個新的迭代器對象。

第3點性質正是可迭代對象能夠重複遍歷的緣由(每次返回一個獨立的迭代器,就能夠保證不一樣的迭代過程不會互相影響);而迭代器因爲返回自身,所以只能遍歷一次。
上面3點能夠經過下面的例子看出來:

from collections import Iterable
from collections import Iterator
print isinstance(iter([1,2]),Iterator)
print isinstance(iter([1,2]),Iterable)
print isinstance([1,2],Iterator)
print isinstance([1,2],Iterable)
##result
True
True
False
True
##id能夠查看一個對象在內存中的地址
test=[1,2,3]
testIter=iter(test)
print id(testIter)
print id(testIter)
print id(iter(test))
print id(iter(test))
print id(test.__iter__())
print id(test.__iter__())
##result:可迭代對象每次調用iter方法都會返回一個新的迭代器對象,而迭代器對象調用iter方法返回自身
67162576 
67162576 
67162688 
67162632 
67162856 
67163024
2.iterable的工做機制

拿一個例子看看,首先定義一個有__iter__方法,可是沒有next()方法的類 (PS:在python2中是next(),python3是__next__()):

from collections import Iterable, Iterator
class Student(object):
 def __init__(self,score):
 self.score=score
 def __iter__(self):
 return iter(self.score)
 
test= Student([80,90,95])
print isinstance(test, Iterable)
print isinstance(test, Iterator)
for i in test:
 print i
##result
True
False
80
90
95
##可重複遍歷
for i in test:
 print i
##result
80
90
95

上面代碼的結果印證了定義中提到的:

缺乏了next()方法,可迭代對象就不是迭代器。

此外,注意到:可迭代對象經過__iter__方法每次都返回了一個獨立的迭代器,這樣就能夠保證不一樣的迭代過程不會互相影響。

也就是說,經過iterable能夠實現重複遍歷,而迭代器是沒法重複遍歷的!

所以,若是想要把可迭代對象轉變爲迭代器,能夠先調用iter()方法返回一個迭代器。而後就能夠用next()不斷迭代了!

print isinstance(iter(test),Iterator)
testIter=iter(test)
print testIter.next()
print testIter.next()
print testIter.next()
##result
True
80
90
95

一旦取完了可迭代對象中全部的元素,再次調用next就會發生異常

print testIter.next()
##result
StopIteration:

3.迭代器Iterator的工做機制

看下面這個例子:

class Student(object):
 def __init__(self,score):
 self.score=score
 def __iter__(self):
 return self
 
 def next(self):
 if self.score<100:
 self.score+=1
 return self.score
 else:
 raise StopIteration()
 
test= Student(90)
print isinstance(test, Iterable)
print isinstance(test, Iterator)
print test.next()
print test.next()
print test.next()
for i in test:
 print i
##result
True
True
91
92
93
94
95
96
97
98
99
100
##若是此時再對test這個迭代器調用next方法,就會拋出異常
test.next()
##result
StopIteration:

這個例子印證了定義中的:迭代器對象必須同時實現__iter__和__next__方法纔是迭代器。

那麼,使用迭代器好處在哪呢?

Python的Iterator對象表示的是一個數據流,Iterator對象能夠被next()函數調用並不斷返回下一個數據,直到沒有數據時拋出StopIteration錯誤。能夠把這個數據流看作是一個有序序列,但咱們卻不能提早知道序列的長度,只能不斷經過next()函數實現按需計算下一個數據,因此Iterator的計算是惰性的,只有在須要返回下一個數據時它纔會計算。

一個很常見的應用就是:Python在處理列表的時候,是直接把整個列表讀進內存的,當遇到大量樣本時的時候會變得很慢。而迭代器的優點在於只把須要的元素讀進內存,所以佔用內存更少。

換句話說,迭代器是一種惰性求值模式,它是有狀態的,只有在調用時才返回值,沒有調用的時候就等待下一次調用。這樣就節省了大量內存空間。

4.for循環的工做機制

有了上面2個例子,就能夠總結一下在可迭代對象與迭代器中的For循環工做機制了。

當對象自己就是迭代器時,For循環工做機制:

1.調用 __iter__方法,返回自身self,也就是返回迭代器。

2.不斷地調用迭代器的next()方法,每次按序返回迭代器中的一個值。

3.迭代到最後沒有元素時,就拋出異常 StopIteration。

在可迭代對象中,for循環工做機制:

1.先判斷對象是否爲可迭代對象(等價於判斷有沒有__iter__或__getitem__方法),沒有的話直接報錯,拋出TypeError異常。有的話,調用 __iter__方法,返回一個迭代器。

2.在python內部不斷地調用迭代器的__next__方法,每次按序返回迭代器中的一個值。

3.迭代到最後沒有元素時,就拋出異常 StopIteration,這個異常 python 本身會處理,不會暴露給開發者。

借用網絡上的一張圖直觀理解一下:

圖片描述

Python中的可迭代對象、迭代器、For循環工做機制、生成器
此外,還要注意,python中的for循環其實兼容了兩種機制:

1.若是對象有__iter__會返回一個迭代器。

2.若是對象沒有__iter__,可是實現了__getitem__,會改用下標迭代的方式。

__getitem__能夠幫助一個對象進行取數和切片操做。

當for發現沒有__iter__可是有__getitem__的時候,會從0開始依次讀取相應的下標,直到發生IndexError爲止,這是一種舊的迭代協議。iter方法也會處理這種狀況,在不存在__iter__的時候,返回一個下標迭代的iterator對象來代替。一個重要的例子是str,字符串就是沒有__iter__方法的,可是卻依然能夠迭代,緣由就是其在for循環時調用了__getitem__方法。

看一個例子:

from collections import Iterable, Iterator
    class Student(object):
     def __init__(self,score):
     self.score=score
     def __getitem__(self,n):
     return self.score[n]
     
    test= Student([80,90,95])
    print isinstance(test, Iterable)
    print isinstance(test, Iterator)
    print isinstance(iter(test), Iterable)
    print isinstance(iter(test), Iterator)
    for i in test:
     print i
    ##result
    False
    False
    True
    True
    80
    90
    95
for i in range(0,3):
     print test[i]
    ##result
    80
    90
    95
for i in iter(test):
     print i
    ##result
    80
    90
    95

能夠看到,實現了__getitem__方法的對象自己,儘管不是iterable與iterator,仍舊是能夠調用for循環的。

經過iter方法,返回一個下標迭代的iterator對象。

5.generator的原理

最後說一下生成器,生成器是一種特殊的迭代器,固然也是可迭代對象。

對於生成器,Python會自動實現迭代器協議,以便應用到迭代中(如for循環,sum函數)。因爲生成器自動實現了迭代器協議,因此,咱們能夠調用它的next方法,而且,在沒有值能夠返回的時候,生成器自動產生StopIteration異常。

建立生成器的方法:將return 改成yield。具體的實現網絡上教程不少,不細說了。

Python學習qun:5563,70268,這裏是python學習者彙集地,歡迎喜歡Python的小夥伴!

6.總結

到一幅圖片很好的描述了本文的全部內容,就拿它做爲文末的總結吧!

圖片描述

相關文章
相關標籤/搜索