說說Python中的iterator,yield表達式及generator,coroutine

每次去看別人寫的代碼都有個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
... 
1
2
3
4
執行方法如下:

    創建對象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: 0
s: 10
i * s 0
10
>>> t.send(11)
i: 1
s: 11
i * s 11
22

一步步講解:

    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。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章