• <td id="ae6ms"><li id="ae6ms"></li></td>
  • <xmp id="ae6ms"><td id="ae6ms"></td><table id="ae6ms"></table>
  • <table id="ae6ms"></table>
  • <td id="ae6ms"></td>
    <td id="ae6ms"></td>
  • <table id="ae6ms"></table><table id="ae6ms"><td id="ae6ms"></td></table>
  • <td id="ae6ms"></td>
  • <table id="ae6ms"><li id="ae6ms"></li></table>
  • <table id="ae6ms"></table>
    西西軟件園多重安全檢測下載網站、值得信賴的軟件下載站!
    軟件
    軟件
    文章
    搜索

    首頁編程開發其它知識 → Python的迭代器和生成器 使用實例及yield的使用

    Python的迭代器和生成器 使用實例及yield的使用

    相關軟件相關文章發表評論 來源:西西整理時間:2015/1/26 15:15:56字體大?。?em class="fontsize">A-A+

    作者:西西點擊:38次評論:0次標簽: Python

    《派森》(Python)3.13 win32 英文安裝版
    • 類型:編程工具大?。?i>21M語言:英文 評分:8.7
    • 標簽:
    立即下載

    迭代器是訪問集合元素的一種方式。迭代器對象從集合的第一個元素開始訪問,直到所有的元素被訪問完結束。迭代器只能往前不會后退,不過這也沒什么,因為人們很少在迭代途中往后退。

    使用迭代器的優點

    對于原生支持隨機訪問的數據結構(如tuple、list),迭代器和經典for循環的索引訪問相比并無優勢,反而丟失了索引值(可以使用內建函數enumerate()找回這個索引值)。但對于無法隨機訪問的數據結構(比如set)而言,迭代器是唯一的訪問元素的方式。

    另外,迭代器的一大優點是不要求事先準備好整個迭代過程中所有的元素。迭代器僅僅在迭代到某個元素時才計算該元素,而在這之前或之后,元素可以不存在或者被銷毀。這個特點使得它特別適合用于遍歷一些巨大的或是無限的集合,比如幾個G的文件,或是斐波那契數列等等。

    迭代器更大的功勞是提供了一個統一的訪問集合的接口,只要定義了__iter__()方法對象,就可以使用迭代器訪問。
     
    迭代器有兩個基本的方法
    next方法:返回迭代器的下一個元素
    __iter__方法:返回迭代器對象本身

    一、迭代器Iterators
    迭代器僅是一容器對象,它實現了迭代器協議。它有兩個基本方法:
    1)next方法
    返回容器的下一個元素
    2)__iter__方法
    返回迭代器自身

    迭代器可使用內建的iter方法創建,見例子:
    >>> i = iter('abc')
    >>> i.next()
    'a'
    >>> i.next()
    'b'
    >>> i.next()
    'c'
    >>> i.next()
    Traceback (most recent call last):
      File "<string>", line 1, in <string>
    StopIteration:

    class MyIterator(object):
      def __init__(self, step):
      self.step = step
      def next(self):
      """Returns the next element."""
      if self.step==0:
      raise StopIteration
      self.step-=1
      return self.step
      def __iter__(self):
      """Returns the iterator itself."""
      return self
    for el in MyIterator(4):
      print el
    --------------------
    結果:
    3
    2
    1
    0

    二、生成器Generators
    從Python2.2起,生成器提供了一種簡潔的方式幫助返回列表元素的函數來完成簡單和有效的代碼。
    它基于yield指令,允許停止函數并立即返回結果。
    此函數保存其執行上下文,如果需要,可立即繼續執行。
    例如Fibonacci函數:
    def fibonacci():
      a,b=0,1
      while True:
      yield b
      a,b = b, a+b
    fib=fibonacci()
    print fib.next()
    print fib.next()
    print fib.next()
    print [fib.next() for i in range(10)]
    --------------------
    結果:
    1
    1
    2
    [3, 5, 8, 13, 21, 34, 55, 89, 144, 233]

    PEP Python Enhancement Proposal Python增強建議

    tokenize模塊
    >>> import tokenize
    >>> reader = open('c:/temp/py1.py').next
    >>> tokens=tokenize.generate_tokens(reader)
    >>> tokens.next()
    (1, 'class', (1, 0), (1, 5), 'class MyIterator(object):/n')
    >>> tokens.next()
    (1, 'MyIterator', (1, 6), (1, 16), 'class MyIterator(object):/n')
    >>> tokens.next()
    (51, '(', (1, 16), (1, 17), 'class MyIterator(object):/n')

    例子:
    def power(values):
      for value in values:
      print 'powering %s' %value
      yield value
    def adder(values):
      for value in values:
      print 'adding to %s' %value
      if value%2==0:
      yield value+3
      else:
      yield value+2
    elements = [1,4,7,9,12,19]
    res = adder(power(elements))
    print res.next()
    print res.next()
    --------------------
    結果:
    powering 1
    adding to 1
    3
    powering 4
    adding to 4
    7

    保持代碼簡單,而不是數據。
    注意:寧可有大量簡單的可迭代函數,也不要一個復雜的一次只計算出一個值的函數。

    例子:
    def psychologist():
      print 'Please tell me your problems'
      while True:
      answer = (yield)
      if answer is not None:
      if answer.endswith('?'):
      print ("Don't ask yourself too much questions")
      elif 'good' in answer:
      print "A that's good, go on"
      elif 'bad' in answer:
      print "Don't be so negative"
    free = psychologist()
    print free.next()
    print free.send('I feel bad')
    print free.send("Why I shouldn't ?")
    print free.send("ok then i should find what is good for me")
    --------------------
    結果:
    Please tell me your problems
    None
    Don't be so negative
    None
    Don't ask yourself too much questions
    None
    A that's good, go on
    None

    雖然很早之前就接觸yield這個詞了,卻一直是一知半解。趁現在有時間,把它研究一通再說。

    含有yield的函數說明它是一個生成器,而不是普通的函數。當程序運行到yield這一行時,該函數會返回值,并保存當前域的所有變量狀態;等到該函數下一次被調用時,會從上一次中斷的地方開始執行,一直遇到下一個yield, 程序返回值, 并在此保存當前狀態; 如此反復,直到函數正常執行完成。

    我一開始還想不明白調用者與生成器之間的函數堆棧是怎么做到的,后來才大悟原來是用到了'協程'這個原理。協程可視為微線程,下面會結合例子來說明一下yield及協程的運行過程。假設定義了test方法:

    [python]

    def test(len):  

        i = 0  

        while i < len :  

            yield i  

            i += 1  

    我們來調用它看看輸出:

    >>> for i in test(5):
    print i


    輸出: 
    0
    1
    2
    3
    4
    這場景是不是很類似 for i in xrange(len); 是的, xrange就是這么干的。 for .. in 的操作實際上是調用了生成器的next()方法,以上的調用過程可以等價為:

    [python]

    f = test(5)  

    print f.next()  

    print f.next()  

    print f.next()  

    print f.next()  

    print f.next()  

    輸出結果與上次輸出一致。
    另外,在這次調用過程中,協程被創建了一次, 被喚醒了5次(通過next),被掛起了5次(通過yield), 最后協程退出并銷毀。 大概就這些點了,有更深的理解再做補充。 

    生成器(Generator)

    如果列表元素可以按照某種算法推算出來,那我們是否可以在循環的過程中不斷推算出后續的元素呢?這樣就不必創建完整的list,從而節省大量的空間。在Python中,這種一邊循環一邊計算的機制,稱為生成器(Generator)。


    要創建一個generator,有很多種方法。第一種方法很簡單,只要把一個列表生成式的[]改成(),就創建了一個generator:


    >>> L = [x * x for x in range(10)]

    >>> L

    [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

    >>> g = (x * x for x in range(10))

    >>> g

    <generator object <genexpr> at 0x104feab40>

    創建L和g的區別僅在于最外層的[]和(),L是一個list,而g是一個generator。

    我們可以直接打印出list的每一個元素,但我們怎么打印出generator的每一個元素呢?

    如果要一個一個打印出來,可以通過generator的next()方法:

    >>> g.next()

    0

    >>> g.next()

    1

    >>> g.next()

    4

    >>> g.next()

    9

    >>> g.next()

    16

    >>> g.next()

    25

    >>> g.next()

    36

    >>> g.next()

    49

    >>> g.next()

    64

    >>> g.next()

    81

    >>> g.next()

    Traceback (most recent call last):

      File "<stdin>", line 1, in <module>

    StopIteration

    我們講過,generator保存的是算法,每次調用next(),就計算出下一個元素的值,直到計算到最后一個元素,沒有更多的元素時,拋出StopIteration的錯誤。

    當然,上面這種不斷調用next()方法實在是太變態了,正確的方法是使用for循環,因為generator也是可迭代對象:

    >>> g = (x * x for x in range(10))

    >>> for n in g:

    ...     print n

    ...

    0

    1

    4

    9

    16

    25

    36

    49

    64

    81

    所以,我們創建了一個generator后,基本上永遠不會調用next()方法,而是通過for循環來迭代它。

    generator非常強大。如果推算的算法比較復雜,用類似列表生成式的for循環無法實現的時候,還可以用函數來實現。

    比如,著名的斐波拉契數列(Fibonacci),除第一個和第二個數外,任意一個數都可由前兩個數相加得到:

    1, 1, 2, 3, 5, 8, 13, 21, 34, ...

    斐波拉契數列用列表生成式寫不出來,但是,用函數把它打印出來卻很容易:

    def fib(max):

        n, a, b = 0, 0, 1

        while n < max:

            print b

            a, b = b, a + b

            n = n + 1

    上面的函數可以輸出斐波那契數列的前N個數:

    >>> fib(6)

    1

    1

    2

    3

    5

    8

    仔細觀察,可以看出,fib函數實際上是定義了斐波拉契數列的推算規則,可以從第一個元素開始,推算出后續任意的元素,這種邏輯其實非常類似generator。

    也就是說,上面的函數和generator僅一步之遙。要把fib函數變成generator,只需要把print b改為yield b就可以了:

    def fib(max):

        n, a, b = 0, 0, 1

        while n < max:

            yield b

            a, b = b, a + b

            n = n + 1

    這就是定義generator的另一種方法。如果一個函數定義中包含yield關鍵字,那么這個函數就不再是一個普通函數,而是一個generator:

    >>> fib(6)

    <generator object fib at 0x104feaaa0>

    這里,最難理解的就是generator和函數的執行流程不一樣。函數是順序執行,遇到return語句或者最后一行函數語句就返回。而變成generator的函數,在每次調用next()的時候執行,遇到yield語句返回,再次執行時從上次返回的yield語句處繼續執行。

    舉個簡單的例子,定義一個generator,依次返回數字1,3,5:

    >>> def odd():

    ...     print 'step 1'

    ...     yield 1

    ...     print 'step 2'

    ...     yield 3

    ...     print 'step 3'

    ...     yield 5

    ...

    >>> o = odd()

    >>> o.next()

    step 1

    1

    >>> o.next()

    step 2

    3

    >>> o.next()

    step 3

    5

    >>> o.next()

    Traceback (most recent call last):

      File "<stdin>", line 1, in <module>

    StopIteration

    可以看到,odd不是普通函數,而是generator,在執行過程中,遇到yield就中斷,下次又繼續執行。執行3次yield后,已經沒有yield可以執行了,所以,第4次調用next()就報錯。

    回到fib的例子,我們在循環過程中不斷調用yield,就會不斷中斷。當然要給循環設置一個條件來退出循環,不然就會產生一個無限數列出來。

    同樣的,把函數改成generator后,我們基本上從來不會用next()來調用它,而是直接使用for循環來迭代:

    >>> for n in fib(6):

    ...     print n

    ...

    1

    1

    2

    3

    5

    8

    小結

    generator是非常強大的工具,在Python中,可以簡單地把列表生成式改成generator,也可以通過函數實現復雜邏輯的generator。

    要理解generator的工作原理,它是在for循環的過程中不斷計算出下一個元素,并在適當的條件結束for循環。對于函數改成的generator來說,遇到return語句或者執行到函數體最后一行語句,就是結束generator的指令,for循環隨之結束。

      相關評論

      閱讀本文后您有什么感想? 已有人給出評價!

      • 8 喜歡喜歡
      • 3 頂
      • 1 難過難過
      • 5 囧
      • 3 圍觀圍觀
      • 2 無聊無聊

      熱門評論

      最新評論

      發表評論 查看所有評論(0)

      昵稱:
      表情: 高興 可 汗 我不要 害羞 好 下下下 送花 屎 親親
      字數: 0/500 (您的評論需要經過審核才能顯示)
      女人让男人桶30分钟免费视频,女人张开腿让男人桶个爽,一进一出又大又粗爽视频
    • <td id="ae6ms"><li id="ae6ms"></li></td>
    • <xmp id="ae6ms"><td id="ae6ms"></td><table id="ae6ms"></table>
    • <table id="ae6ms"></table>
    • <td id="ae6ms"></td>
      <td id="ae6ms"></td>
    • <table id="ae6ms"></table><table id="ae6ms"><td id="ae6ms"></td></table>
    • <td id="ae6ms"></td>
    • <table id="ae6ms"><li id="ae6ms"></li></table>
    • <table id="ae6ms"></table>