課時49:魔法方法:生成器

那是個好男孩發表於2018-08-26

目錄:

  一、生成器

  二、課時49課後習題及答案

 

*********************

一、生成器

**********************

生成器的學習並不涉及魔法方法,甚至它巧妙地避開了類和物件,僅通過普通地函式就可以實現了。

生成器其實是迭代器的一種實現。

  • 生成器的發明一方面是為了使得Python更為簡潔,因為,迭代器需要我們自己去定義一個類和實現相關的方法,而生成器則只需要在普通的函式中加上一個yield語句即可。
  • 另一方面,生成器的發明,使得Python模仿協同程式的概念得以實現。所謂協同程式,就是可以執行得獨立函式呼叫,函式可以暫停或者掛起,並在需要得時候從程式離開得地方繼續或者重新開始。

對於呼叫一個普通的Python函式,一般是從函式的第一行程式碼開始執行,結束於return語句、異常、或者函式所有語句執行完畢。一旦函式將控制權交還給呼叫者,就意味著全部結束。函式中做的所有工作以及儲存在區域性變數中的資料都將丟失。再次呼叫這個函式時,一切將從頭建立。

Python是通過生成器來實現類似於協同程式的概念:生成器可以暫時掛起函式,並保留函式的區域性變數等資料,然後在再次呼叫它的時候,從上次暫停的位置繼續執行下去。

舉個例子:

 

>>> def myGen():
    print("生成器被執行!")
    yield 1
    yield 2

    
>>> myG = myGen()
>>> next(myG)
生成器被執行!
1
>>> next(myG)
2
>>> next(myG)
Traceback (most recent call last):
  File "<pyshell#9>", line 1, in <module>
    next(myG)
StopIteration

正如大家所見,當函式結束的時候,一個StopIteration異常就會丟擲。由於Python的for迴圈會自動呼叫next()方法和處理StopIteration異常,所以for迴圈當然也是可以對生成器產生作用的:

>>> for i in myGen():
    print(i)

    
生成器被執行!
1
2

像前面介紹的斐波那契的例子,也可以用生成器來實現:

>>> def fibs():
    a = 0
    b = 1
    while True:
        a,b = b,a + b
        yield a

        

>>> for each in fibs():
    if each > 100:
        break
    print(each)

    
1
1
2
3
5
8
13
21
34
55
89

事到如今,你應該已經很好的掌握了列表推到式子(並沒有,哭唧唧),那下邊這個列表推導式表達的是啥意思:

>>> a = [i for i in range(100) if not(i%2) and i%3]

其實上邊這個列表推導式求得就是100以內,能被2整除但不能被3整除的所有整數:

>>> a
[2, 4, 8, 10, 14, 16, 20, 22, 26, 28, 32, 34, 38, 40, 44, 46, 50, 52, 56, 58, 62, 64, 68, 70, 74, 76, 80, 82, 86, 88, 92, 94, 98]

python3除了有列表推導式,還有字典推導式:

>>> b = {i:i%2 == 0 for i in range(10)}
>>> b
{0: True, 1: False, 2: True, 3: False, 4: True, 5: False, 6: True, 7: False, 8: True, 9: False}

還有集合推導式:

>>> c = {i for i in [1,1,2,3,3,4,5,5,5,6,7,7,8]}
>>> c
{1, 2, 3, 4, 5, 6, 7, 8}

那是否有字串推導式和元組推導式呢?試一試:

>>> d = "I love zww"
>>> d
'I love zww'

噢,不行,因為在雙引號內,所有的東西都變成了字串,所以不存在字串推導式,那元組推導式呢?

>>> e = (i for i in range(10))
>>> e
<generator object <genexpr> at 0x000002001EC4CB88>

咦?似乎這個不是什麼推導式嘛。generator,多麼熟悉的單詞,就是生成器嘛!沒錯用普通小括號括起來的正是生成器推導式,來證明一下:

>>> next(e)
0
>>> next(e)
1
>>> next(e)
2
>>> next(e)
3
>>> next(e)
4

用for語句把剩下的給列印出來:

>>> for each in e:
    print(each)

    
5
6
7
8
9

還有一個特性更牛,生成器推導式如果作為函式的引數,可以直接寫推導式,而不用加小括號:

>>> sum(i for i in range(100) if i % 2)
2500

【擴充套件閱讀】提高你的 Python:解釋 yield 和 Generators(生成器)

 

*******************************

二、課時49課後習題及答案

*******************************

 

 預知後文如何?請君耐心等待.

 

相關文章