日本好好热aⅴ|国产99视频精品免费观看|日本成人aV在线|久热香蕉国产在线

  • <cite id="ikgdy"><table id="ikgdy"></table></cite>
    1. 西西軟件園多重安全檢測下載網(wǎng)站、值得信賴的軟件下載站!
      軟件
      軟件
      文章
      搜索

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

      Python的迭代器和生成器 使用實(shí)例及yield的使用

      相關(guān)軟件相關(guān)文章發(fā)表評(píng)論 來源:西西整理時(shí)間:2015/1/26 15:15:56字體大。A-A+

      作者:西西點(diǎn)擊:38次評(píng)論:0次標(biāo)簽: Python

      《派森》(Python)3.13 win32 英文安裝版
      • 類型:編程工具大小:21M語言:英文 評(píng)分:8.7
      • 標(biāo)簽:
      立即下載

      迭代器是訪問集合元素的一種方式。迭代器對(duì)象從集合的第一個(gè)元素開始訪問,直到所有的元素被訪問完結(jié)束。迭代器只能往前不會(huì)后退,不過這也沒什么,因?yàn)槿藗兒苌僭诘局型笸恕?/p>

      使用迭代器的優(yōu)點(diǎn)

      對(duì)于原生支持隨機(jī)訪問的數(shù)據(jù)結(jié)構(gòu)(如tuple、list),迭代器和經(jīng)典for循環(huán)的索引訪問相比并無優(yōu)勢,反而丟失了索引值(可以使用內(nèi)建函數(shù)enumerate()找回這個(gè)索引值)。但對(duì)于無法隨機(jī)訪問的數(shù)據(jù)結(jié)構(gòu)(比如set)而言,迭代器是唯一的訪問元素的方式。

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

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

      一、迭代器Iterators
      迭代器僅是一容器對(duì)象,它實(shí)現(xiàn)了迭代器協(xié)議。它有兩個(gè)基本方法:
      1)next方法
      返回容器的下一個(gè)元素
      2)__iter__方法
      返回迭代器自身

      迭代器可使用內(nèi)建的iter方法創(chuàng)建,見例子:
      >>> 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
      --------------------
      結(jié)果:
      3
      2
      1
      0

      二、生成器Generators
      從Python2.2起,生成器提供了一種簡潔的方式幫助返回列表元素的函數(shù)來完成簡單和有效的代碼。
      它基于yield指令,允許停止函數(shù)并立即返回結(jié)果。
      此函數(shù)保存其執(zhí)行上下文,如果需要,可立即繼續(xù)執(zhí)行。
      例如Fibonacci函數(shù):
      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)]
      --------------------
      結(jié)果:
      1
      1
      2
      [3, 5, 8, 13, 21, 34, 55, 89, 144, 233]

      PEP Python Enhancement Proposal Python增強(qiáng)建議

      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()
      --------------------
      結(jié)果:
      powering 1
      adding to 1
      3
      powering 4
      adding to 4
      7

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

      例子:
      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")
      --------------------
      結(jié)果:
      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這個(gè)詞了,卻一直是一知半解。趁現(xiàn)在有時(shí)間,把它研究一通再說。

      含有yield的函數(shù)說明它是一個(gè)生成器,而不是普通的函數(shù)。當(dāng)程序運(yùn)行到y(tǒng)ield這一行時(shí),該函數(shù)會(huì)返回值,并保存當(dāng)前域的所有變量狀態(tài);等到該函數(shù)下一次被調(diào)用時(shí),會(huì)從上一次中斷的地方開始執(zhí)行,一直遇到下一個(gè)yield, 程序返回值, 并在此保存當(dāng)前狀態(tài); 如此反復(fù),直到函數(shù)正常執(zhí)行完成。

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

      [python]

      def test(len):  

          i = 0  

          while i < len :  

              yield i  

              i += 1  

      我們來調(diào)用它看看輸出:

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


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

      [python]

      f = test(5)  

      print f.next()  

      print f.next()  

      print f.next()  

      print f.next()  

      print f.next()  

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

      生成器(Generator)

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


      要?jiǎng)?chuàng)建一個(gè)generator,有很多種方法。第一種方法很簡單,只要把一個(gè)列表生成式的[]改成(),就創(chuàng)建了一個(gè)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>

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

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

      如果要一個(gè)一個(gè)打印出來,可以通過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保存的是算法,每次調(diào)用next(),就計(jì)算出下一個(gè)元素的值,直到計(jì)算到最后一個(gè)元素,沒有更多的元素時(shí),拋出StopIteration的錯(cuò)誤。

      當(dāng)然,上面這種不斷調(diào)用next()方法實(shí)在是太變態(tài)了,正確的方法是使用for循環(huán),因?yàn)間enerator也是可迭代對(duì)象:

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

      >>> for n in g:

      ...     print n

      ...

      0

      1

      4

      9

      16

      25

      36

      49

      64

      81

      所以,我們創(chuàng)建了一個(gè)generator后,基本上永遠(yuǎn)不會(huì)調(diào)用next()方法,而是通過for循環(huán)來迭代它。

      generator非常強(qiáng)大。如果推算的算法比較復(fù)雜,用類似列表生成式的for循環(huán)無法實(shí)現(xiàn)的時(shí)候,還可以用函數(shù)來實(shí)現(xiàn)。

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

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

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

      def fib(max):

          n, a, b = 0, 0, 1

          while n < max:

              print b

              a, b = b, a + b

              n = n + 1

      上面的函數(shù)可以輸出斐波那契數(shù)列的前N個(gè)數(shù):

      >>> fib(6)

      1

      1

      2

      3

      5

      8

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

      也就是說,上面的函數(shù)和generator僅一步之遙。要把fib函數(shù)變成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的另一種方法。如果一個(gè)函數(shù)定義中包含yield關(guān)鍵字,那么這個(gè)函數(shù)就不再是一個(gè)普通函數(shù),而是一個(gè)generator:

      >>> fib(6)

      <generator object fib at 0x104feaaa0>

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

      舉個(gè)簡單的例子,定義一個(gè)generator,依次返回?cái)?shù)字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不是普通函數(shù),而是generator,在執(zhí)行過程中,遇到y(tǒng)ield就中斷,下次又繼續(xù)執(zhí)行。執(zhí)行3次yield后,已經(jīng)沒有yield可以執(zhí)行了,所以,第4次調(diào)用next()就報(bào)錯(cuò)。

      回到fib的例子,我們在循環(huán)過程中不斷調(diào)用yield,就會(huì)不斷中斷。當(dāng)然要給循環(huán)設(shè)置一個(gè)條件來退出循環(huán),不然就會(huì)產(chǎn)生一個(gè)無限數(shù)列出來。

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

      >>> for n in fib(6):

      ...     print n

      ...

      1

      1

      2

      3

      5

      8

      小結(jié)

      generator是非常強(qiáng)大的工具,在Python中,可以簡單地把列表生成式改成generator,也可以通過函數(shù)實(shí)現(xiàn)復(fù)雜邏輯的generator。

      要理解generator的工作原理,它是在for循環(huán)的過程中不斷計(jì)算出下一個(gè)元素,并在適當(dāng)?shù)臈l件結(jié)束for循環(huán)。對(duì)于函數(shù)改成的generator來說,遇到return語句或者執(zhí)行到函數(shù)體最后一行語句,就是結(jié)束generator的指令,for循環(huán)隨之結(jié)束。

        相關(guān)評(píng)論

        閱讀本文后您有什么感想? 已有人給出評(píng)價(jià)!

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

        熱門評(píng)論

        最新評(píng)論

        發(fā)表評(píng)論 查看所有評(píng)論(0)

        昵稱:
        表情: 高興 可 汗 我不要 害羞 好 下下下 送花 屎 親親
        字?jǐn)?shù): 0/500 (您的評(píng)論需要經(jīng)過審核才能顯示)