說說Python中的iterator,yield運算式及generator,coroutine,iteratorcoroutine
每次去看別人寫的代碼都有個yield,感覺超級geek,今天花點兒時間整理下。
按照順序來:
1. iterator
iterator叫做迭代器,用來遍曆可以序列化的資料,比如一個list,set 等,當然如果對象想要能夠使用迭代器來遍曆,只要在該對象的類中添加__iter__()方法,該方法返回一個迭代器對象,迭代器對象中需要實現next()方法,例子如下:
>>> class sequenceClass(object):... def __init__(self, *args):... self._data = list(args)... def __iter__(self):... return DataIter(self)...>>> class DataIter(object):... def __init__(self, data):... self._index = 0... self._data = data._data... def next(self):... if self._index >= len(self._data): raise StopIteration()... d = self._data[self._index]... self._index += 1... return d... >>> data = sequenceClass(1,2,3,4)>>> for x in data: print x... 1234
執行方法如下:
建立對象data : data = sequenceClass(1,2,3,4)
使用for迴圈遍曆data:
1.第一次for迴圈遍曆data時,調用了data的__iter__()方法,該方法返回一個迭代器對象:DataIter
2.接著for迴圈每次調用DataIter對象的next()方法,next()方法返回資料
3.等到資料遍曆完成時,拋出異常StopIteration, 該異常被for迴圈撲獲,遍曆結束。
以上是iterator的實現方法,但有一種更加簡單的方式,但初學時可能不太好理解,就是使用yield運算式來遍曆對象。
2.yield運算式及genetator
Python中,凡是含有yield的函數,被調用時都返回一個generator對象。generator設計的目的是能夠更加簡單的實現迭代器協議,例如上面的例子,需要單獨寫一個類來遍曆資料,而用generator,直接一個yield運算式就可以用做迭代器了。
看一個例子:
>>> def test_generator():... s = 0... for i in xrange(10):... s = yield i * s... print 'i:', i... print 's:', s... print 'i * s', i * s... >>> t = test_generator()>>> t<generator object test_generator at 0x10403a820>>>> >>> t.next()0>>> t.send(10)i: 0s: 10i * s 010>>> t.send(11)i: 1s: 11i * s 1122
一步步講解:
1.定義了一個函數叫做test_generator
2.調用test_generator,得到t對象
3.運行t時,是一個generator對象
4.t.next()或者t.send(None)啟動generator對象, generator對象運行到yield,將yield右面的運算式的值返回,並儲存generator對象當前的狀,因此,後面的列印都沒有被執行。
5.t.send(10)
1)將輸入的值10賦值給s
2)執行yield後面的運算式,即將所有print執行完畢,解析如下:
由於在第4步generator儲存了對象的當前的狀態,因此: i = 0, s是剛輸入的值為10, i *s 當然就為0了,接著進入下一次迴圈,此時i= 1,執行yield運算式,結果得10,儲存當前generator狀態,返回10,generator停止運行。
6.t.send(11)
1)和第5步類似了,將11輸入給s
2)因為第5步,generator儲存了當前的對象的值,因此: i = 1, s是剛輸入的值為11, i *s 當然就為11了,接著進入下一次迴圈,此時i= 2,執行yield運算式,結果得22,儲存當前generator狀態,返回22,generator停止運行。
7.接下來在調用t.send(num),和第5,6步一樣,直到generator內部的迴圈完成,當再次調用時,將拋出異常:StopIteration
總結一下:
1. yield運算式, 四種形式:
a. 不接受輸入值或者輸入值是None
yield 1
b. 接受輸入值
s = yield 1
c. 接受輸入,但不返回資料,這樣預設返回None
s = yield
d.既不接受輸入,也不返回值,預設返回None
yield
第一種:當函數調用到yield時,返回yield的右邊經過計算的值 ,這裡說計算的意思是,yield後面可以寫成函數,運算式等,
第二種:當函數調用到yield時,必須傳入一個值,該值存入s中,然後返回yield後面的運算式的值並儲存目前狀態
第三種:只是將資料接受進來,然後執行yield後的語句,再次執行到yield時,儲存目前狀態並返回,這樣的用例一般是
只列印一些處理訊息,而不需要結果的方式。
第四種:這樣的只能遍曆generator內部的資料了。
2.使用generator的好處:
1)可以很方便的實現迭代器,即yield不接受輸入值
2)使用generator可以減少記憶體的使用,因為遍曆出來的值都是每次進行計算的,不用一次性全部計算完成,然後一個個
遍曆,最終釋放掉;另外可以在yield後面做一些操作,可以很隨意的控制返回值。
3.coroutine 協程
使用yield的函數,若用send來控制,就算coroutine了,若用for迴圈來控制,就算generator或者iterator,使用方法大多見於
decorator, 如tornado中的gen.coroutine。