国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

《Python有什么好學(xué)的》之生成器/迭代器

n7then / 3091人閱讀

摘要:為什么是斐波那契談到生成器迭代器,人們總是喜歡用斐波那契數(shù)列來(lái)舉例。那么,換句話來(lái)說(shuō),即能由推導(dǎo)式得出的數(shù)列,其實(shí)都可以用來(lái)做生成器迭代器的例子。然而,生成器和生成器類的實(shí)例都屬于迭代器。

“Python有什么好學(xué)的”這句話可不是反問(wèn)句,而是問(wèn)句哦。

主要是煎魚(yú)覺(jué)得太多的人覺(jué)得Python的語(yǔ)法較為簡(jiǎn)單,寫出來(lái)的代碼只要符合邏輯,不需要太多的學(xué)習(xí)即可,即可從一門其他語(yǔ)言跳來(lái)用Python寫(當(dāng)然這樣是好事,誰(shuí)都希望入門簡(jiǎn)單)。

于是我便記錄一下,如果要學(xué)Python的話,到底有什么好學(xué)的。記錄一下Python有什么值得學(xué)的,對(duì)比其他語(yǔ)言有什么特別的地方,有什么樣的代碼寫出來(lái)更Pythonic。一路回味,一路學(xué)習(xí)。

為什么是斐波那契

談到生成器/迭代器,人們總是喜歡用斐波那契數(shù)列來(lái)舉例。

斐波那契數(shù)列,數(shù)學(xué)表示為a(1)=0, a(2)=1, a(i)=a(i-1)+a(i-2) (i>=3):
0 1 1 2 3 5 8 13 21 ...
用一句話說(shuō),就是第三位數(shù)起,當(dāng)前這位數(shù)是前兩位的和。

當(dāng)然,使用斐波那契來(lái)舉例是一個(gè)很合適的選擇。

那么,為什么說(shuō)到生成器/迭代器就喜歡斐波那契,而不是其他呢?

斐波那契數(shù)列有一個(gè)特征:當(dāng)前這位數(shù)的值,可以通過(guò)前兩位數(shù)的值推導(dǎo)出來(lái)。比如,我知道了第n位數(shù)是5,第n+1位數(shù)是8,那我就可以輕易地得出,第n+2位必然是5+8=13。

即,斐波那契數(shù)是可以通過(guò)推導(dǎo)式得出的。

就是這樣一種類似于冥冥注定的感覺(jué):當(dāng)前的平行空間,是由你之前的選擇決定的。而且是欽定好的,你接下來(lái)的每一步,其實(shí)都已經(jīng)被決定了,由什么決定呢,由你以前走過(guò)的路決定的。

那么,換句話來(lái)說(shuō),即能由推導(dǎo)式得出的數(shù)列,其實(shí)都可以用來(lái)做生成器/迭代器的例子。例如,煎魚(yú)用一條式子y(n)=y(n-1)^2 + 1 (n>=1), y(1)=1,一樣能拿來(lái)當(dāng)例子。

既然這樣,那就用y(n)=y(n-1)^2 + 1 (n>=1), y(1)=1吧。

原始低效的循環(huán)

一開(kāi)始,人們的思想很簡(jiǎn)單,如果要求y(n)=y(n-1)^2 + 1 (n>=1), y(1)=1中y數(shù)列的第13位,即y(13),那很簡(jiǎn)單,就輪詢到13個(gè)就好了。

def y():
    y_current = 1
    n = 1
    while n < 13:
        y_current = y_current ** 2 + 1
        n += 1
        print(n, y_current)

輸出也挺長(zhǎng)的,就截個(gè)圖算了:

這個(gè)時(shí)候,這個(gè)代碼是完全夠用的。接下來(lái),煎魚(yú)加點(diǎn)需求(PM般的獰笑):

暴露n的值,n值可以作為參數(shù)輸入,即我需要控制數(shù)列計(jì)算到哪

函數(shù)返回(輸出)整個(gè)數(shù)列

接下來(lái),函數(shù)改成:

def y(n_max):
    y_current = 0
    n = 0
    ret_list = []
    while n < n_max:
        y_current = y_current ** 2 + 1
        n += 1
        ret_list.append(y_current)

    return ret_list


if __name__ == "__main__":
    for i in y(13):
        print(i)

看起來(lái)沒(méi)什么毛病,完全符合要求。但是,問(wèn)題出現(xiàn)在當(dāng)函數(shù)的參數(shù)n_max較大的時(shí)候。要多大呢,煎魚(yú)嘗試輸出n_max=13000000,即:

if __name__ == "__main__":
    for i in y(13000000):
        print(i)

我們看得出來(lái),這個(gè)函數(shù)的計(jì)算量十分大,以煎魚(yú)當(dāng)前的電腦配置(Macbook pro 2017 i5 8G RAM),等了一兩分鐘還沒(méi)結(jié)束,只好強(qiáng)行中斷了。

程序?yàn)槭裁纯敲淳媚亍R驗(yàn)樵诤瘮?shù)的邏輯中,程序試圖將13000000個(gè)值都計(jì)算出來(lái)再返回list以供外接輪詢,而且這13000000個(gè)一個(gè)比一個(gè)難算(越來(lái)越大,指數(shù)增長(zhǎng))。同時(shí),該list也占了龐大的內(nèi)存空間。

到了這個(gè)時(shí)候,煎魚(yú)終于要引入生成器了。

生成器的小試牛刀

其實(shí)煎魚(yú)就加入了一個(gè)yield,并稍作修改:

def y_with_yield(n_max):
    y_current = 0
    n = 0
    while n < n_max:
        y_current = y_current ** 2 + 1
        n += 1
        yield y_current
        
if __name__ == "__main__":
    for i in y_with_yield(13000000):
        print(i)

雖然屏幕滾動(dòng)得很慢,但是起碼是在實(shí)時(shí)地滾動(dòng)的。

加入了yield變成這樣,其實(shí)就是搞成了一個(gè)簡(jiǎn)單的生成器。在這里,生成器的作用有:

for i in y_with_yield(13000000)的循環(huán)中,每一次循環(huán)程序才會(huì)進(jìn)入函數(shù)去計(jì)算,而不會(huì)把全部結(jié)果都計(jì)算出來(lái)再返回

由于不會(huì)把全部結(jié)果都計(jì)算出來(lái)再返回,程序運(yùn)行所需的內(nèi)存也大幅地減少

暫時(shí)給出初步結(jié)論:

這個(gè)小生成器在較大數(shù)據(jù)的計(jì)算量時(shí),有較大的優(yōu)勢(shì)

程序把推導(dǎo)式的計(jì)算通過(guò)yield分散了,降低了cpu和內(nèi)存的壓力

如果沒(méi)有煎魚(yú)的需求,這一切都白搭且多余

我們?cè)賮?lái)看下生成器的其他用途吧。

通過(guò)緩存機(jī)制讀取文件

在讀文件或處理文件時(shí)使用緩存是很有必要的,因?yàn)槲覀兛偸遣恢牢募?huì)有多大,文件的大小會(huì)不會(huì)把程序給拖垮。

煎魚(yú)新建一個(gè)文件(假設(shè)叫test.txt),并往其中寫入文本,運(yùn)行以下代碼:

def read_file(file_path):

    BLOCK_SIZE = 100
    with open(file_path, "rb") as f:
        while True:
            block = f.read(BLOCK_SIZE)
            if block:
                yield block
            else:
                return


if __name__ == "__main__":
    for i in read_file("./test.txt"):
        print(i)
        print("--------------block-split--------------")

我們把100個(gè)長(zhǎng)度分為一個(gè)block,這個(gè)block就相當(dāng)于我們的緩存:先從文件中讀100個(gè),然后讓程序處理這100個(gè)字符(此處處理為print),再讀下一個(gè)100。其中block-split的輸出是為了讓我們更好地辯識(shí)出block的頭尾。

生成器類和生成器對(duì)象

通過(guò)yield瞎搞出來(lái)的簡(jiǎn)易生成器有一個(gè)很大的限制,就是必須要在循環(huán)內(nèi)。

雖然“迭代”和“循環(huán)”有關(guān)聯(lián),但是當(dāng)生成器的生成邏輯無(wú)比復(fù)雜時(shí),比如“推導(dǎo)”的方法已經(jīng)無(wú)法用數(shù)學(xué)推導(dǎo)式表達(dá)時(shí),或者某種場(chǎng)景下的業(yè)務(wù)邏輯比較復(fù)雜以至于無(wú)法直接通過(guò)循環(huán)表達(dá)時(shí),生成器類來(lái)了。

生成器類看起來(lái)很簡(jiǎn)單,其實(shí)就是將煎魚(yú)在上面寫的簡(jiǎn)單生成器寫成一個(gè)類。

重點(diǎn)就是,我們得找到“推導(dǎo)”,推導(dǎo)在這里是指next函數(shù) —— 我們實(shí)現(xiàn)的生成器類最重要的就是next()。

我們來(lái)實(shí)現(xiàn)上面的y函數(shù)的生成器類:

class Y(object):

    def __init__(self, n_max):
        self.n_max = n_max
        self.n = 0
        self.y = 0

    def __iter__(self):
        return self

    def next(self):
        if self.n < self.n_max:
            self.y = self.y ** 2 + 1
            self.n += 1
            return self.y
        raise StopIteration()


if __name__ == "__main__":
    y = Y(13)
    for i in y:
        print(i)

有幾點(diǎn)是需要注意的:

類需要包含__iter__()函數(shù),而返回的不一定是self,但是需要生成器

實(shí)現(xiàn)next方法,“迭代”的邏輯請(qǐng)放到該函數(shù)里面

如果需要引入別的庫(kù)或者寫別的函數(shù),可以在類中隨便加

接下來(lái),煎魚(yú)帶來(lái)一段很無(wú)聊的表演,來(lái)表示__iter__()函數(shù)而返回的不一定是self:

class SuperY(object):

    def __init__(self, n_max):
        self.n_max = n_max

    def __iter__(self):
        return Y(self.n_max)

if __name__ == "__main__":
    sy = SuperY(13)
    for i in sy:
        print(i)

這段代碼的輸出和上一段一模一樣。

生成器的照妖鏡

這里照妖鏡的意思,指一個(gè)能鑒別某對(duì)象(甚至不是對(duì)象)是否一個(gè)生成器的東西。

說(shuō)起來(lái),可能會(huì)有點(diǎn)多余而且零碎。

其中有三個(gè)函數(shù):

isgeneratorfunction(),字面意思,是否生成器函數(shù)

isgenerator(),還是字面意思,是否生成器

isinstance(),這個(gè)指某對(duì)象是否為某個(gè)類的實(shí)例

我們把前面寫過(guò)的y(帶yield的函數(shù)),和Y(生成器類)導(dǎo)入后,進(jìn)行實(shí)驗(yàn)觀察:

from use_yield import y_with_yield as y
from iter_obj import Y
from inspect import isgeneratorfunction, isgenerator
from types import GeneratorType
from collections import Iterator

if __name__ == "__main__":
    print(isgeneratorfunction(y))  # True
    print(isgeneratorfunction(Y))  # False
    print(isgeneratorfunction(y(5)))  # False
    print(isgeneratorfunction(Y(5)))  # False
    print(isgenerator(y))  # False
    print(isgenerator(Y))  # False
    print(isgenerator(y(5)))  # True
    print(isgenerator(Y(5)))  # False

    print("")

    print(isinstance(y, GeneratorType))  # False
    print(isinstance(y(5), GeneratorType))  # True
    print(isinstance(Y, GeneratorType))  # False
    print(isinstance(Y(5), GeneratorType))  # False

    print("")

    print(isinstance(y, Iterator))  # False
    print(isinstance(y(5), Iterator))  # True
    print(isinstance(Y, Iterator))  # False
    print(isinstance(Y(5), Iterator))  # True

實(shí)驗(yàn)的結(jié)論為:

帶yield的y函數(shù)是一個(gè)生成器函數(shù),帶yield的y函數(shù)帶上參數(shù)5后,稱為了生成器。因?yàn)閥是函數(shù)的引用,而帶上了參數(shù)5后,y(5)就不再是函數(shù)了,而是通過(guò)yield進(jìn)化成了生成器。

生成器是類GeneratorType的實(shí)例,但很遺憾的是,生成器類的實(shí)例不是生成器(黑人問(wèn)號(hào))。

然而,生成器y(5)和生成器類的實(shí)例都屬于迭代器。

其他亂七八糟的

Python里面,range和xrange有什么不同,用哪個(gè)更好,為什么?

對(duì)的,就是和生成器有關(guān)系,嘿嘿。

先這樣吧

若有錯(cuò)誤之處請(qǐng)指出,更多地請(qǐng)關(guān)注造殼。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/44954.html

相關(guān)文章

  • Python什么好學(xué)修飾

    摘要:然后煎魚(yú)加了一個(gè)后再調(diào)用函數(shù),得到的輸出結(jié)果和加修飾器的一樣,換言之等效于因此,我們對(duì)于,可以理解是,它通過(guò)閉包的方式把新函數(shù)的引用賦值給了原來(lái)函數(shù)的引用。 Python有什么好學(xué)的這句話可不是反問(wèn)句,而是問(wèn)句哦。 主要是煎魚(yú)覺(jué)得太多的人覺(jué)得Python的語(yǔ)法較為簡(jiǎn)單,寫出來(lái)的代碼只要符合邏輯,不需要太多的學(xué)習(xí)即可,即可從一門其他語(yǔ)言跳來(lái)用Python寫(當(dāng)然這樣是好事,誰(shuí)都希望入門簡(jiǎn)...

    lewinlee 評(píng)論0 收藏0
  • Python什么好學(xué)上下文管理

    摘要:引上下文管理器太極生兩儀,兩儀為陰陽(yáng)。而最常用的則是,即上下文管理器使用上下文管理器用之后的文件讀寫會(huì)變成我們看到用了之后,代碼沒(méi)有了創(chuàng)建,也沒(méi)有了釋放。實(shí)現(xiàn)上下文管理器我們先感性地對(duì)進(jìn)行猜測(cè)。現(xiàn)實(shí)一個(gè)上下文管理器就是這么簡(jiǎn)單。 Python有什么好學(xué)的這句話可不是反問(wèn)句,而是問(wèn)句哦。 主要是煎魚(yú)覺(jué)得太多的人覺(jué)得Python的語(yǔ)法較為簡(jiǎn)單,寫出來(lái)的代碼只要符合邏輯,不需要太多的學(xué)習(xí)即可...

    qpwoeiru96 評(píng)論0 收藏0
  • Python進(jìn)階:設(shè)計(jì)模式迭代模式

    摘要:抓住了迭代器模式的本質(zhì),即是迭代,賦予了它極高的地位。輸出結(jié)果輸出結(jié)果小結(jié)迭代器模式幾乎是種設(shè)計(jì)模式中最常用的設(shè)計(jì)模式,本文主要介紹了是如何運(yùn)用迭代器模式,并介紹了模塊生成迭代器的種方法,以及種生成迭代器的內(nèi)置方法。 showImg(https://segmentfault.com/img/bVbmv7W?w=4272&h=2848); 在軟件開(kāi)發(fā)領(lǐng)域中,人們經(jīng)常會(huì)用到這一個(gè)概念——設(shè)...

    pubdreamcc 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<