可迭代物件
在介紹迭代器之前,我們得先引入可迭代物件(iterable)的概念。可迭代物件可以直接作用於for迴圈。例如常見的列表、字典和元組都是可迭代物件。例如我們可以使用for迴圈依次輸出列表的值。
事實上,只要物件實現了__iter__()方法,我們就可以對該物件進行迭代。該方法返回了乙個迭代器物件。x=[1,2,3,4]
for i in x:
print(i)
迭代器
迭代器(iterator)就是實現了__next__()方法的物件,當呼叫該方法時,迭代器會返回它的下乙個值。可迭代物件不一定是迭代器,如list、dict等,它們雖然可迭代,但是並不能稱作迭代器。而迭代器是可迭代物件。當然,我們也可以通過iter()方法獲得乙個迭代器。
既然列表等也可進行迭代,為什麼還要引入迭代器的概念呢?這是因為使用列表等可迭代物件是一次性獲取所有的值。當列表中有100萬個值時,就會占用十分大的記憶體。而迭代器可以乙個乙個的獲取值。我們以生成斐波那契數列為例,寫乙個生成該數列的迭代器。x=[1,2,3,4]
y=iter(x)
y.__next__()
fib既是乙個可迭代物件,因為它實現了__iter__()方法,同時它又是乙個迭代器,因為它實現了__next__()方法。當我們需要值時,呼叫__next__()方法向呼叫者返回結果,但沒有呼叫時它便處於休眠狀態。class fib:
def __init__(self):
self.prev=0
self.curr=1
def __iter__(self):
return self
def __next__(self):
value=self.curr
self.curr+=self.prev
self.prev=value
return value
f=fib()
for i in range(5):
print(f.__next__())
生成器(generator)
生成器是python新引入的概念,它是一種用普通的函式語法定義的迭代器。注意,生成器也是一種迭代器。它比迭代器更加優雅。生成器的實現是採用了yield的關鍵字,任何包含yield語句的函式稱為生成器。我們還是用乙個例子來說明生成器的用法。
fib函式看樣子就像是乙個普通函式,除了它沒有使用return關鍵字,而是使用了yield。呼叫該函式後返回的是乙個生成器物件。此時函式體內的**並沒有執行,只有當呼叫了__next__()後才執行**。返回的生成器結果如下所示。def fib():
prev,curr=0,1
while true:
yield curr
prev,curr=curr,curr+prev
f=fib()
print(f)
for i in range(5):
print(f.__next__())
一般的函式執行完後會return乙個值然後退出,而生成器遇到yield關鍵字後會向呼叫者返回乙個值,並且自動掛起,同時還保留著當前的狀態,今後需要時將呼叫send()或__next__()喚醒並繼續執行。每乙個生成器都有__next__()方法,它要麼返回迭代的下一項,要麼引起異常結束迭代。
除了__next__()方法之外,還有send()也可以喚醒程式並繼續執行,與__next__()不同的是,它傳送乙個資訊到生成器內部。下面依舊通過乙個例子講解send()和__next__()的執行過程。
這裡參照了 的例子,寫的十分詳細。
寫到這,還有乙個問題需要明確,那麼為什麼list等是可迭代物件,卻不是迭代器呢?def gen():
print("start") #*0
m = yield 2 #*1 # yield意思是產生, 此表示產生2 就是呼叫可以返回2 (當然,返回是有條件的
print(m) #*2
n = yield 3 #*3
print(n) #*4
try: #*5
print("end") #*6
except stopiteration as e: #*7
print(e) #*8
# raise e
\# 先說明yield表示式, yield有點像《前置操作符》, 跟在表示式前面,用於把表示式的值返回給
\#呼叫者, (yield 2)本身的值賦給m
g = gen()
""" result:
"""\#↑>>> 可以看出是乙個generator, 此時並沒有執行 *0和*1行
\#g.send(3)
""" result:
typeerror: can't send non-none value to a just-started generator
"""\#↑>>> 開始生成器不能send非空值
print("--------------")
g.send(none) # 住: g.__next__()相當於g.send(none)
""" result:
start
2 #此時函式(生成器)返回了2(就是yield後的,此時你可以理解為yield相當於return,但是返回後暫停了執行)
"""\# 此時函式掛起(讓出cpu使用權)並保持狀態,直到遇到下乙個__next__/send
\# g.send(none) 呼叫者把none這個訊息send給上乙個yield表示式,由於第一次呼叫,沒有上乙個yield.
\# 到目前為止, 呼叫了一次send(none) 總結一下, 上面說到保持狀態:目前函式執行到*1行並掛起
print("--------------")
g.send(5)
print("--------------")
""" result:
53 #和上面一樣,第二個yield生成(返回)的值為3
"""\# 此時函式從*1行後繼續執行 通過呼叫g.send(5),呼叫者把5這個訊息send給上乙個yield表示式(yield 2)
\#既:(yield 2)這個整體的值,此時m = 5,到目前為止, 呼叫了一次send(none),一次send(5) 總結一下, 上面
\#說到保持狀態:1.此時的狀態是:m(既yield 2這個整體的結果值)被呼叫者傳送訊息賦值為5 2.目前函式執行
\#到*3行並掛起
print(g.send(6))
\#result:
\# 6
\# end
\# stopiteration
\# 此時函式從*3行後繼續執行 通過呼叫g.send(6),呼叫者把6這個訊息send給了上乙個yield表示式(yield 3)
\#既:(yield 3)這個整體的值,此時n = 6,繼續往下執行,由於沒有了遇不到yield,所有會出現 stopiteration
\#異常而結束。到目前為止,生成器函式執行結束,由於生成器會記住狀態, 再呼叫send/__next__還會出現stopiteration異常
\# 通過生成器, 呼叫者和函式內部可以互動訊息, 暫停執行, 可用同步**處理非同步, 如:tornado的 @gen修飾器,
\#twisted的@inlinecallback修飾器...
這是因為python的iterator物件表示的是乙個資料流,iterator物件可以被__next__()函式呼叫並不斷返回下乙個資料,直到沒有資料時丟擲stopiteration錯誤。可以把這個資料流看做是乙個有序序列,但我們卻不能提前知道序列的長度,只能不斷通過next()函式實現按需計算下乙個資料,所以iterator的計算是惰性的,只有在需要返回下乙個資料時它才會計算。
總結
(1)生成器(generator)能夠迭代的關鍵是他有__next__()方法,工作原理就是通過重複呼叫__next__()方法,直到捕獲乙個異常。
(3)send()和__next__()的區別就在於send可傳遞引數給yield表示式,這時候傳遞的引數就會作為yield表示式的值,而yield的引數是返回給呼叫者的值,也就是說send可以強行修改上乙個yield表示式值。
(4)第一次呼叫時候必須先__next__()或send(),否則會報錯,send後之所以為none是因為這時候沒有上乙個yield,所以也可以認為__next__()等同於send(none)
Python迭代器和生成器
先說迭代器,對於string list dict tuple等這類容器物件,使用for迴圈遍歷是很方便的。在後台for語句對容器物件呼叫iter 函式,iter 是python的內建函式。iter 會返回乙個定義了next 方法的迭代器物件,它在容器中逐個訪問容器內元素,next 也是python的...
Python迭代器和生成器
迭代器是訪問集合元素的一種方法 是可以記住遍歷的位置的物件。迭代器物件從集合的第乙個元素開始訪問,直到所有的元素被訪問 他有兩個基本的方法,iter 和next 字串,列表或遠足物件都可以用於建立迭代器 list1 1,2,3,4 it1 iter list1 建立迭代器物件 print next ...
python 迭代器和生成器
迭代器是訪問集合元素的一種方式。迭代器物件從集合的第乙個元素開始訪問,直到所有的元素被訪問完結束。迭代器只能往前不會後退,不過這也沒什麼,因為人們很少在迭代途中往後退。另外,迭代器的一大優點是不要求事先準備好整個迭代過程中所有的元素。迭代器僅僅在迭代到某個元素時才計算該元素,而在這之前或之後,元素可...