2020年9月17日星期四

[課後作業] 第049講:亂入:生成器|課後測試題及答案

[課後作業] 第049講:亂入:生成器|課後測試題及答案




《零基礎入門學習Python》視頻下載地址:傳送門
T&f!uxcE1MSjt~w%J8A]a
測試題(筆試,不能上機哦~):
Y0.!r|LCny8 {lAuqfI_$WSQbEXH;
0.通常,一般的函數從第一行代碼開始執行,並在什麼情況下結束?mZQcMEJ[I
M3#Dvsg`|;zW8%dHmVJy+'
Sy >8.*#L{Tu9V"lj,?]bCo)!5<;
1.什麼是協同程序?Powered by bbs.fishc.com
U+^W %hMylQP!-2=CG#s94tf
o0f>i5F~;|7%C_(MHW4bs
2.生成器所能實現的任何操作都可以由迭代器來代替嗎,為什麼?t`pH=!
.UgRlM&<+ wv[iAH1`;%2T|dQ'CN6
g0!1B?oJtCTL[2^8H64;%
3.將一個函數改造為生成器,說白了就是把什麼語句改為yield語句?!1?_>L] e
GJzPK6)H|AM-(XbUV&qCdmyIn
2e9>%}+[o~({F|.HjB^sk7zi;#
4.說到底,生成器的最大作用是什麼?版權屬於:bbs.fishc.com
nX3mukeaK<6*!RUc{. ;x
6eGn!qiTJdzOM20kZ9 7H*xR+&_
5.如下,get_prime()是一個獲得素數的生成器,請問第2行代碼while True有何作用?
ex-%v!<

  1. def get_primes(number):
  2.     while True:
  3.         if is_prime(number):
  4.             yield number
  5.         number += 1
複製代碼

H|JtE<gr3m7KN5-ikhT?aG`X@VSu
動動手(一定要自己動手試試哦~):
TunCx!6)pRk"I7Plahy':jz^-@
0.要求實現一個功能與reversed( )相同(內置函數reversed(seq)是返回一個迭代器,是序列seq的逆序顯示)的生成器。例如:qX"!; [)
  1. >>> for i in myRev("FishC"):
  2.     print(i, end='')

  3. ChsiF
複製代碼

BJtRl^k<x>iH|qC-{(#ewg5%nyc
1. 10以內的素數之和是:2 + 3 + 5 + 7 = 17,那麼請編寫程序,計算2000000以內的素數之和?dZWJG8
ak'h!05gv%LZXQtc.?


2.請寫下這一節課你學習到的內容:格式不限,回憶並複述是加強記憶的好方式!ozCU)nj-qD2Zkm]|BGvhT+f&(6dI.


請務必自己先動手,貪一時之快先看答案,您將失去一次鍛煉的機會。omLZtHwy%6
an4p0m2v_JS57lWDY %kX{GAL<N"#F
回复您的答案即可查看參考答案!
 hR2S4|UGd
6`1k7wPN|x<3@WAEvB}n5ZsbY^FKuz
z^|h5+#d!3]v`K?~T ;nW"ra
測試題答案:Powered by bbs.fishc.com

本帖隱藏的內容

版權屬於:bbs.fishc.com
0.通常,一般的函數從第一行代碼開始執行,並在什麼情況下結束?$f<Uchx>=
xhnSKra]py_e.2-[ 8u9!w+E
答:對於調用一個普通的Python函數,一般是從函數的第一行代碼開始執行,結束於return語句、異常或者函數所有語句執行完畢。一旦函數將控制權交還給調用者,就意味著全部結束。函數中做的所有工作以及保存在局部變量中的數據都將丟失。如果再次調用這個函數時,一切都將重新開始。?]{Vne
G],@InF;_i=QNA5hM}^o&Kcx.
l5q67c;^ZSF*n#be}V?mB'|)x,=
1.什麼是協同程序?Powered by bbs.fishc.com
Xw"GI2)7OBk jq-&,C$3:YKv0JL4}
答:所謂的協同程序就是可以運行的獨立函數調用,函數可以暫停或者掛起,並在需要的時候從程序離開的地方繼續或者重新開始。{q"'*aOM-
}7i*t6e^Roz'|xc(OY ,
Python是通過生成器來實現類似於協同程序的概念:生成器可以暫時掛起函數,並保留函數的局部變量等數據,然後在再次調用它的時候,從上次暫停的位置繼續執行下去。Ea<1XI
V%)XZ"@4{JSsoQg9#:r<W
B-XzV)8vbUs.7* 6HGf!mF| ~C&+o?
2.生成器所能實現的任何操作都可以由迭代器來代替嗎,為什麼?_fnXi8v3]g
NFGl{tD s%`K#]mI(d6je
答:是的,都可以。因為生成器事實上就是基於迭代器來實現的,生成器只需要一個yield語句即可,但它內部會自動創建__iter__()和__next__()方法。<MH>vDS`
a1<0$w+DhVGqx(&%?2@peyR`tI
Jt6>.1<qpCFsnkoX*QP$#V
3.將一個函數改造為生成器,說白了就是把什麼語句改為yield語句?>N@<pUi
4N3vp~zE^?$.D0m=<,7i%RZbtCkQS
答:return語句。版權屬於:bbs.fishc.com
[gknNjIp_DZUi4O %'lm^w$
D@H|BrQAE`xVOzh02uq$"=-)a}+c~
4.說到底,生成器的最大作用是什麼?N5<@9Qw
;8N"~Gz42VPrRpa&myti
答:使得函數可以“保留現場”,當下一次執行該函數是從上一次結束的地方開始,而不是重頭再來。F&wA9
jki~D7P){eY=gda*0|(CZq^
'?Qs+ai|u1NZWA^M*( v%=!ITFDo
5.如下,get_prime()是一個獲得素數的生成器,請問第2行代碼while True有何作用?ulI%R7v<
  1. def get_primes(number):
  2.     while True:
  3.         if is_prime(number):
  4.             yield number
  5.         number += 1
複製代碼

答:這個while True循環是用來確保生成器函數永遠也不會執行到函數末尾的。只要調用next()這個生成器就會生成一個值。這是一個處理無窮序列的常見方法(這類生成器也是很常見的)。rkzP6<C}

來自:bbs.fishc.com
0PLKMSHkzDj{?]rQt p&lY$f7%|>(n
動動手答案:版權屬於:bbs.fishc.com

本帖隱藏的內容

Powered by bbs.fishc.com
0.要求實現一個功能與reversed()相同(內置函數reversed(seq)是返回一個迭代器,是序列seq的逆序顯示)的生成器。例如:t:+5C;I*
  1. >>> for i in myRev("FishC"):
  2.     print(i, end='')

  3. ChsiF
複製代碼

代碼清單:Powered by bbs.fishc.com
  1. def myRev(data):
  2.     # 這裡用range 生成data 的倒序索引
  3.     # 注意,range 的結束位置是不包含的
  4.     for index in range(len(data)-1, -1, -1):
  5.         yield data[index]
複製代碼

qA~Fz5er@kSpaD2K}m^YG&)?XuwC
1. 10以內的素數之和是:2 + 3 + 5 + 7 = 17,那麼請編寫程序,計算2000000以內的素數之和?q6v>fj%<;n
~)k`<=lm*_s!>[tzxD|Rrd
答:如果你的策略是將2000000以內的所有素數找到並存放到一個列表中,再依次進行求和計算,那麼這個列表極有可能會撐爆你的內存。所以這道題就必須用到生成器來實現啦。m">#=QF8
[(dL1sX-]MA}tKW0F6`+enRwp
詳細解釋請參考:提高你的Python:解釋yield和Generators(生成器);@yP$q[S
fUoV&mTrR[Z5'2qc-HJ6DE* ab(B
結果是142913828922,你答對了嗎?!S={rh
L`R~#8 2ln!AzkG$_6>v%
代碼清單:版權屬於:bbs.fishc.com
  1. import math

  2. def is_prime(number):
  3.     if number > 1:
  4.         if number == 2:
  5.             return True
  6.         if number % 2 == 0:
  7.             return False
  8.         for current in range(3, int(math.sqrt(number) + 1), 2):
  9.             if number % current == 0:
  10.                 return False
  11.         return True
  12.     return False

  13. def get_primes(number):
  14.     while True:
  15.         if is_prime(number):
  16.             yield number
  17.         number += 1

  18. def solve():
  19.     total = 2
  20.     for next_prime in get_primes(3):
  21.         if next_prime < 2000000:
  22.             total += next_prime
  23.         else:
  24.             print(total)
  25.             return

  26. if __name__ == '__main__':
  27.     solve()
複製代碼

3.請寫下這一節課你學習到的內容:格式不限,回憶並複述是加強記憶的好方式!

小甲魚希望你認真對待作業就像你希望小甲魚推出高質量視頻一樣渴望^_^

因為上一節課給大家介紹了迭代器,這一節課繼續給大家介紹生成器,雖然說生成器和迭代器可以說是Python近幾年來引入的最強大的兩個概念,但是生成器的學習並不涉及到高級的魔法方法,甚至巧妙的避開了類和對象,僅需要通過普通的函數就可以實現了,由於生成器的概念比較高級,所以在之前的函數章節並沒有講到它,因為那時候大家還只是基礎階段,學習就是需要一個漸進的過程。像上一節課的迭代器,很多人學了之後就感覺很簡單,但是如果我們把迭代器放到循環那一章節來講,大家勢必就會一頭霧水了。
正如剛才所說,生成器事實上是迭代器的一種實現,那既然迭代器可以實現,那為什麼還要生成器呢?有一句老話說得好:“存在即合理”。生成器的發明一方面就是使得Python更加簡潔,因為迭代器需要我們去定義一個類和實現相關的方法,才可以定義一個靈活的迭代器。而生成器需要在普通的函數加上一個yield 語句。另外一個重要的方面就是生成器的發明使得Python模仿協同程序的概念得以實現。
所謂的協同程序就是可以運行的獨立函數的調用,函數可以暫停或者掛起,並在需要的時候從程序離開的地方繼續或者重新開始。我們知道,對於一個普通的函數來說,我們調用它一般都是從函數的第一句開始走,結束用return語句或者異常或者函數所有的語句執行完畢。一旦函數將控制權交還給調用者,那就意味著全部都結束了,因為我們說過:函數的所有的工作、保存都是局部變量,局部變量在調用結束都會自動消失。
而生成器就是一個特殊的函數,調用可以中斷、可以暫停,暫停之後他把控制權臨時交出來,然後交完之後,在需要的時候他又能夠獲取回來,重新獲得控制權,從上一次暫停的時候繼續下去。這就是生成器(Generator)。
多說不如實幹,給大家舉個例子:
  1. >>> def myGen():
  2. print("生成器被执行!")
  3. yield 1
  4. yield 2
  5. >>> myG = myGen()
  6. >>> next(myG)
  7. 生成器被执行!
  8. 1
  9. >>> next(myG)
  10. 2
  11. >>> next(myG)
  12. Traceback (most recent call last):
  13. File "<pyshell#29>", line 1, in <module>
  14. next(myG)
  15. StopIteration
一旦一個函數里面出現yield 語句,那麼這個函數就被定義為生成器,yield 相當於函數里面的return 語句,但是普通函數的return 一返回,這個函數就結束了,但是對於生成器來說,出現yield ,它就會把yield 後面的參數給返回,然後就暫停在這個yield 的位置,下一次執行就從這裡開始。如程序執行所示,第一次next() 時,就打印了1,第二次next() 就繼續打印了2,然後就沒有了,如果繼續next() 就拋出了StopIteration 的異常。
Python的for 循環會自動調用next() 方法和探測StopIteration 結束,如下:
  1. >>> for i in myGen():
  2. print(i)
  3. 生成器被执行!
  4. 1
  5. 2
我們上節課斐波那契數列的例子,也可以用生成器來實現:
  1. >>> def fibs():
  2. a = 0
  3. b = 1
  4. while True:
  5. a, b = b, a + b
  6. yield a
看上去,好像是死循環,因為永遠為True,但是這是一個生成器,隨時都可以被暫停,只要yield, 就返回,就暫停了。
我們用一個for 語句把它打印出來,我們這裡設置一下,如果大於100的話,就跳出循環,要不就會一直走下去:
  1. >>> for each in fibs():
  2. if each > 100:
  3. break
  4. print(each, end = ' ')
  5. 1 1 2 3 5 8 13 21 34 55 89
事到如今,我們應該已經很好的掌握了列表推導式:
  1. >>> a = [i for i in range(100) if not(i % 2) and (i % 3)]
  2. >>> a
  3. [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]
在列表裡加一個for 語句就是列表推導式,上面的就是求100以內能被2整除,但是不能被3整除的整數。
這裡想告訴大家的是,Python3 除了有列表推導式之外,還有字典推導式:
  1. >>> b = {i : i % 2 == 0 for i in range(10)}
  2. >>> b
  3. {0: True, 1: False, 2: True, 3: False, 4: True, 5: False, 6: True, 7: False, 8: True, 9: False}
這可以得到0-9 這10個數字是否為偶數,如果是偶數,i % 2 == 0 就是True。
還有集合推導式:
  1. >>> c = {i for i in [1, 2, 3, 2, 4, 3, 6, 5]}
  2. >>> c
  3. {1, 2, 3, 4, 5, 6}
照這種情形想下去,很多人就會以為會有字符串推導式和元組推導式。我們試一下:
  1. >>> d = "i for i in 'I love Python!'"
  2. >>> d
  3. "i for i in 'I love Python!'"
顯然,對於字符串推導式,我們是不可能實現的,因為出現雙引號,它就會把裡面的內容如實打印出來,因為這就是字符串。
那麼對於所謂的元組推導式呢?
  1. >>> e = (i for i in range(10))
  2. >>> e
  3. <generator object <genexpr> at 0x00000235B13EECA8>
顯然,打印的不是元組(tuple),這裡沒有打印數據,但是出現了generator (生成器),沒錯,這裡我們鋪墊了這麼久,就是為了告訴大家,我們這裡用圓括號括起來的,就是生成器推導式。我們來證明一下:
  1. >>> next(e)
  2. 0
  3. >>> next(e)
  4. 1
  5. >>> next(e)
  6. 2
  7. >>> for each in e:
  8. print(each)
  9. 3
  10. 4
  11. 5
  12. 6
  13. 7
  14. 8
  15. 9
生成器推導式如果作為函數的參數,可以直接寫推導式就可以了,不用加圓括號,
例如,如果我們對上面的生成器推導式求和,應該是:
  1. >>> sum((i for i in range(10)))
  2. 45
但是生成器推導式如果作為函數的參數,不用加圓括號也是可以的:
  1. >>> sum(i for i in range(10))
  2. 45
但是單獨的生成器推導式,是一定需要括號的,這是基本的語法要求:
  1. >>> i for i in range(10)
  2. SyntaxError: invalid syntax
關於生成器的技術要點,大家可以繼續參閱->  解釋yield和Generators(生成器)Powered by bbs.fishc.com

0 留言:

發佈留言