在MPGI4的第二次作業(yè)中,出現(xiàn)了 xrange() 相關(guān)用法,在查找其與 range 有什么不同時,發(fā)現(xiàn)了 generator 和 yield 等一系列概念,特此總結(jié)。
yield 的用法起源于對一般 function 中 return 的擴(kuò)展。在一個 function 中,必須有一個返回值列于 return 之后,可以返回數(shù)字也可以返回空值,但是必須要有一個返回值,標(biāo)志著這個 function 的結(jié)束。一旦它結(jié)束,那么這個 function 中產(chǎn)生的一切變量將被統(tǒng)統(tǒng)拋棄,有什么可以使一個 function 暫停下來,并且返回當(dāng)前所在地方的值,當(dāng)接收到繼續(xù)的命令時可以繼續(xù)前進(jìn)呢?換個說法,就是 return 返回一個值,并且記住這個返回的位置。這個操作有點兒像
for x in range():
pass
這個循環(huán)語句中 range() 這個函數(shù)干的事情,它給出一個數(shù),當(dāng)循環(huán)完一次以后再在前一個數(shù)的基礎(chǔ)上拿出另外一個數(shù)。但是這里的函數(shù)其實是自動生成一個 list 然后從這個 list 第一個開始往后一個一個給出,那么這種方法的劣勢顯而易見——會有生成一個 list 。也就是說不管你的 for 循環(huán)用幾回,它都會生成這個 list (比如你有一個龐大的數(shù)據(jù)庫,那么光是生成一個表單就需要很長時間,然而也許你要用的東西就在前幾個,那么你建起這個檢索目錄遠(yuǎn)遠(yuǎn)超過你實際所需要的)。這時就是 xrange() 登場的時候了,它就是一個不需要建立 list 卻一樣完成任務(wù)的好命令。那么 xrange() 是什么呢?
xrange() 其實是一個 generator 生成器,不同于一般的 function,里面就包含著yield。generator 人如其名,是一個生成器,生成什么呢?不妨把它看作生成一組你需要的序列的函數(shù),通常情況下我們只需要自然數(shù)這種簡單的序列,但是如果讓你生成一組斐波那契數(shù)列呢?這組數(shù)列是無窮的,你想沿著這個數(shù)列一直算下去,直到算到你滿意的一個數(shù),但是如果你也不知道這個數(shù)在哪里,那么你就不能提前給出一個這個數(shù)列,因為你不知道在哪里會停下來。面對這個問題時,最好的解決方法就是記住當(dāng)前的數(shù),記住當(dāng)前的地址,查看一個這個數(shù)符合不符合要求,如果不符合要求,那么繼續(xù)從這個數(shù)/地址開始計算下一個斐波那契數(shù)字。這就是 yield 要干的事情了。(其實這么做的好處還有一個就是減少斐波那契數(shù)列的運(yùn)算量,這個數(shù)列因為是自循環(huán)數(shù)列,所以如果按照循環(huán)算法的話需要很大的運(yùn)算量,但是如果是采用動態(tài)紀(jì)錄法的話,那么就會非常簡單。這屬于優(yōu)化算法范疇,暫且按下不表。)下面就是斐波那契數(shù)列用 yield 的實現(xiàn)法:
# 斐波那契數(shù)列:每一個數(shù)都等于前兩個數(shù)之和
def fib(to=10):
curr = 0
next = 1
count = 0
while count < to:
yield curr
curr = next
next = curr + next
count += 1
# 一個用法例子:每一個斐波那契數(shù)列的數(shù)加1
if __name__ == '__main__':
for x in fib(20):
print(x+1)
注意:yield 一旦被采用,那么def后面的代碼會立馬被認(rèn)作是一個 generator 而不是一個 function,因為生成的是一個 generator,所以可以被用在 for in 這個循環(huán)語句中。而 generator 和 function 從本質(zhì)上是不同的,兩者的區(qū)別就涉及到了next()和send()這兩個函數(shù)的用法。
如果把這個python文件命名為fi.py,并運(yùn)行之。那么可以瞬間得到結(jié)果:
? Desktop python fi.py
1
2
3
5
9
17
33
65
129
257
513
1025
2049
4097
8193
16385
32769
65537
131073
262145
用這個方法可以自己研究很多有趣的數(shù)學(xué)問題,比如找出2萬以內(nèi)的質(zhì)數(shù)之和,找出大于10的0次方,一次方,...,n次方的最小質(zhì)數(shù)。你不需要建立一個很大的索引庫,只需要一個一個數(shù)字算下去就好。
這篇Jeff的文章幫了我一些忙,但可能是我英語不好,雖然解釋的很基礎(chǔ),但對于我來說,還是沒有徹底理解yield 和與其配套的next()和send()的用法,以至于我拼命想搞懂他們的用法和關(guān)系并且成功了,不過回過頭來看,確實解釋的很全面。我覺得這種基礎(chǔ)全面式解釋需要一個實際的用法例子來補(bǔ)充說明,從稍微高一點兒的地方,或者從另一個初學(xué)者的視角來解釋他們,初學(xué)者并不要解釋的太基礎(chǔ),因為初學(xué)者其實學(xué)編程語言時根本不基礎(chǔ),越往后學(xué)習(xí)才會涉及到編譯器的某些基礎(chǔ)概念,這也是這篇文章的立意之處。
未完待續(xù):next()和send()這兩個函數(shù)的用法。