久久精品国产亚洲高清|精品日韩中文乱码在线|亚洲va中文字幕无码久|伊人久久综合狼伊人久久|亚洲不卡av不卡一区二区|精品久久久久久久蜜臀AV|国产精品19久久久久久不卡|国产男女猛烈视频在线观看麻豆

    1. <style id="76ofp"></style>

      <style id="76ofp"></style>
      <rt id="76ofp"></rt>
      <form id="76ofp"><optgroup id="76ofp"></optgroup></form>
      1. 千鋒教育-做有情懷、有良心、有品質(zhì)的職業(yè)教育機(jī)構(gòu)

        手機(jī)站
        千鋒教育

        千鋒學(xué)習(xí)站 | 隨時(shí)隨地免費(fèi)學(xué)

        千鋒教育

        掃一掃進(jìn)入千鋒手機(jī)站

        領(lǐng)取全套視頻
        千鋒教育

        關(guān)注千鋒學(xué)習(xí)站小程序
        隨時(shí)隨地免費(fèi)學(xué)習(xí)課程

        當(dāng)前位置:首頁(yè)  >  技術(shù)干貨  > Python的函數(shù)式編程與閉包

        Python的函數(shù)式編程與閉包

        來(lái)源:千鋒教育
        發(fā)布人:xqq
        時(shí)間: 2023-11-07 14:02:46 1699336966

        函數(shù)式編程

              函數(shù)式編程這個(gè)概念我們可能或多或少都聽(tīng)說(shuō)過(guò),剛聽(tīng)說(shuō)的時(shí)候不明覺(jué)厲,覺(jué)得這是一個(gè)非常黑科技的概念。但是實(shí)際上它的含義很樸實(shí),但是延伸出來(lái)許多豐富的用法。

        在早期編程語(yǔ)言還不是很多的時(shí)候,我們會(huì)將語(yǔ)言分成高級(jí)語(yǔ)言與低級(jí)語(yǔ)言。比如匯編語(yǔ)言,就是低級(jí)語(yǔ)言,幾乎什么封裝也沒(méi)有,做一個(gè)賦值運(yùn)算還需要我們手動(dòng)調(diào)用寄存器。而高級(jí)語(yǔ)言則從這些面向機(jī)器的指令當(dāng)中抽身出來(lái),轉(zhuǎn)而面向過(guò)程或者是對(duì)象。也就是說(shuō)我們寫(xiě)代碼面向的是一段計(jì)算過(guò)程或者是一個(gè)計(jì)算機(jī)當(dāng)中抽象出來(lái)的對(duì)象。如果你學(xué)過(guò)面向?qū)ο?,你?huì)發(fā)現(xiàn)和面向過(guò)程相比,面向?qū)ο蟮某橄蟪潭雀吡艘恍?,做了更加完善的封裝。

        在面向?qū)ο笾竽?,我們還可以做什么封裝和抽象呢?這就輪到了函數(shù)式編程。

        函數(shù)我們都了解,就是我們定義的一段程序,它的輸入和輸出都是確定的。我們把一段函數(shù)寫(xiě)好,它可以在任何地方進(jìn)行調(diào)用。既然函數(shù)這么好用,那么能不能把函數(shù)也看成是一個(gè)變量進(jìn)行返回和傳參呢?

        OK,這個(gè)就是函數(shù)式編程最直觀的特點(diǎn)。也就是說(shuō)我們寫(xiě)的一段函數(shù)也可以作為變量,既可以用來(lái)賦值,還可以用來(lái)傳遞,并且還能進(jìn)行返回。這樣一來(lái),大大方便了我們的編碼,但是這并不是有利無(wú)害的,相反它帶來(lái)許多問(wèn)題,最直觀的問(wèn)題就是由于函數(shù)傳入的參數(shù)還可以是另一個(gè)函數(shù),這會(huì)導(dǎo)致函數(shù)的計(jì)算過(guò)程變得不可確定,許多超出我們預(yù)期的事情都有可能發(fā)生。

        所以函數(shù)式編程是有利有弊的,它的確簡(jiǎn)化了許多問(wèn)題,但也產(chǎn)生了許多新的問(wèn)題,我們?cè)谑褂玫倪^(guò)程當(dāng)中需要謹(jǐn)慎。

        傳入、返回函數(shù)在我們之前介紹filter、map、reduce以及自定義排序的時(shí)候,其實(shí)我們已經(jīng)用到了函數(shù)式編程的概念了。

        比如在我們調(diào)用sorted進(jìn)行排序的時(shí)候,如果我們傳入的是一個(gè)對(duì)象數(shù)組,我們希望根據(jù)我們制定的字段排序,這個(gè)時(shí)候我們往往需要傳入一個(gè)匿名函數(shù),用來(lái)制定排序的字段。其實(shí)傳入的匿名函數(shù),其實(shí)就是函數(shù)式編程最直觀的體現(xiàn)了:

        sorted(kids,key=lambdax:x['score'])

        除此之外,我們還可以返回一個(gè)函數(shù),比如我們來(lái)看一個(gè)例子:

        defdelay_sum(nums):

        defsum():

        s=0

        foriinnums:

        s+=i

        returns

        returnsum

        如果這個(gè)時(shí)候我們調(diào)用delay_sum傳入一串?dāng)?shù)字,我們會(huì)得到什么?

        答案是一個(gè)函數(shù),我們可以直接輸出,從打印信息里看出這一點(diǎn):

        >>>delay_sum([1,3,4,2])

        .sumat0x1018659e0>

        我們想獲得這個(gè)運(yùn)算結(jié)果應(yīng)該怎么辦呢?也很簡(jiǎn)單,我們用一個(gè)變量去接收它,然后執(zhí)行這個(gè)新的變量即可:

        >>>f=delay_sum([1,3,4,2])

        >>>f()

        10

        這樣做有一個(gè)好處是我們可以延遲計(jì)算,如果不使用函數(shù)式編程,那么我們需要在調(diào)用delay_sum這個(gè)函數(shù)的時(shí)候就計(jì)算出結(jié)果。如果這個(gè)運(yùn)算量很小還好,如果這個(gè)運(yùn)算量很大,就會(huì)造成開(kāi)銷。并且當(dāng)我們計(jì)算出結(jié)果來(lái)之后,這個(gè)結(jié)果也許不是立即使用的,可能到很晚才會(huì)用到。既然如此,我們返回一個(gè)函數(shù)代替了運(yùn)算,當(dāng)后面真正需要用到的時(shí)候再執(zhí)行結(jié)果,從而延遲了運(yùn)算。這也是很多計(jì)算框架的常用思路,比如spark。

        閉包我們?cè)賮?lái)回顧一下我們剛才舉的例子,在剛才的delay_sum函數(shù)當(dāng)中,我們內(nèi)部實(shí)現(xiàn)了一個(gè)sum函數(shù),我們?cè)谶@個(gè)函數(shù)當(dāng)中調(diào)用了delay_sum函數(shù)傳入的參數(shù)。這種對(duì)外部作用域的變量進(jìn)行引用的內(nèi)部函數(shù)就稱為閉包。

        其實(shí)這個(gè)概念很形象,因?yàn)檫@個(gè)函數(shù)內(nèi)部調(diào)用的數(shù)據(jù)對(duì)于調(diào)用方來(lái)說(shuō)是封閉的,完全是一個(gè)黑盒,除非我們查看源碼,否則我們是不知道它當(dāng)中數(shù)據(jù)的來(lái)源的。除了不知道來(lái)源之外,更重要的是它引用的是外部函數(shù)的變量,既然是變量就說(shuō)明是動(dòng)態(tài)的。也就是說(shuō)我們可以通過(guò)改變某些外部變量的值來(lái)改變閉包的運(yùn)行效果。

        這么說(shuō)有點(diǎn)拗口,我們來(lái)看一個(gè)簡(jiǎn)單的例子。在Python當(dāng)中有一個(gè)函數(shù)叫做math.pow其實(shí)就是計(jì)算次方的。比如我們要計(jì)算x的平方,那么我們應(yīng)該這樣寫(xiě):

        math.pow(x,2)

        但是如果我們當(dāng)前場(chǎng)景下只需要計(jì)算平方,我們每次都要傳入額外再傳入一個(gè)2會(huì)顯得非常麻煩,這個(gè)時(shí)候我們使用閉包,可以簡(jiǎn)化操作:

        defmypow(num):

        defpw(x):

        returnmath.pow(x,num)

        returnpw

        pow2=mypow(2)

        print(pow2(10))

        通過(guò)閉包,我們把第二個(gè)變量給固定了,這樣我們只需要使用pow2就可以實(shí)現(xiàn)原來(lái)math.pow(x,2)的功能了。如果我們突然需求變更需要計(jì)算3次方或者是4次方,我們只需要修改mypow的傳入?yún)?shù)即可,完全不需要修改代碼。

        實(shí)際上這也是閉包最大的使用場(chǎng)景,我們可以通過(guò)閉包實(shí)現(xiàn)一些非常靈活的功能,以及通過(guò)配置修改一些功能等操作,而不再需要通過(guò)代碼寫(xiě)死。要知道對(duì)于工業(yè)領(lǐng)域來(lái)說(shuō),線上的代碼是不能隨便變更的,尤其是客戶端,比如applestore或者是安卓商店當(dāng)中的軟件包,只有用戶手動(dòng)更新才會(huì)拉取。如果出現(xiàn)問(wèn)題了,幾乎沒(méi)有辦法修改,只能等用戶手動(dòng)更新。所以常規(guī)操作就是使用一些類似閉包的靈活功能,通過(guò)修改配置的方式改變代碼的邏輯。

        除此之外閉包還有一個(gè)用處是可以暫存變量或者是運(yùn)行時(shí)的環(huán)境。

        舉個(gè)例子,我們來(lái)看下面這段代碼:

        defstep(x=0):

        x+=5

        returnx

        這是沒(méi)有使用閉包的函數(shù),不管我們調(diào)用多少次,答案都是5,執(zhí)行完x+=5之后的結(jié)果并不會(huì)被保存起來(lái),當(dāng)函數(shù)返回了,這個(gè)暫存的值也就被拋棄了。那如果我希望每次調(diào)用都是依據(jù)上次調(diào)用的結(jié)果,也就是說(shuō)我們每次修改的操作都能保存起來(lái),而不是丟棄呢?

        這個(gè)時(shí)候就需要使用閉包了:

        deftest(x=0):

        defstep():

        nonlocalx

        x+=5

        returnx

        returnstep

        t=test()

        t()

        >>>5

        t()

        >>>10

        也就是說(shuō)我們的x的值被存儲(chǔ)起來(lái)了,每次修改都會(huì)累計(jì),而不是丟棄。這里需要注意一點(diǎn),我們用到了一個(gè)新的關(guān)鍵字叫做nonlocal,這是Python3當(dāng)中獨(dú)有的關(guān)鍵字,用來(lái)申明當(dāng)前的變量x不是局部變量,這樣Python解釋器就會(huì)去全局變量當(dāng)中去尋找這個(gè)x,這樣就能關(guān)聯(lián)上test方法當(dāng)中傳入的參數(shù)x。Python2官方已經(jīng)不更新了,不推薦使用。

        由于在Python當(dāng)中也是一切都是對(duì)象,如果我們把閉包外層的函數(shù)看成是一個(gè)類的話,其實(shí)閉包和類區(qū)別就不大了,我們甚至可以給閉包返回的函數(shù)關(guān)聯(lián)函數(shù),這樣幾乎就是一個(gè)對(duì)象了。來(lái)看一個(gè)例子:

        defstudent():

        name='xiaoming'

        defstu():

        returnname

        defset_name(value):

        nonlocalname

        name=value

        stu.set_name=set_name

        returnstu

        stu=student()

        stu.set_name('xiaohong')

        print(stu())

        最后運(yùn)算的結(jié)果是xiaohong,因?yàn)槲覀冋{(diào)用set_name改變了閉包外部的值。這樣當(dāng)然是可以的,但是一般情況下我們并不會(huì)用到它。和寫(xiě)一個(gè)class相比,通過(guò)閉包的方法運(yùn)算速度會(huì)更快。原因比較隱蔽,是因?yàn)殚]包當(dāng)中沒(méi)有self指針,從而節(jié)省了大量的變量的訪問(wèn)和運(yùn)算,所以計(jì)算的速度要快上一些。但是閉包搞出來(lái)的偽對(duì)象是不能使用繼承、派生等方法的,而且和正常的用法格格不入,所以我們知道有這樣的方法就可以了,現(xiàn)實(shí)中并不會(huì)用到。

        閉包的坑閉包雖然好用,但是不小心的話也是很容易踩坑的,下面介紹幾個(gè)常見(jiàn)的坑點(diǎn)。

        閉包不能直接訪問(wèn)外部變量

        這一點(diǎn)我們剛才已經(jīng)提到了,在閉包當(dāng)中我們不能直接訪問(wèn)外部的變量的,必須要通過(guò)nonlocal關(guān)鍵字進(jìn)行標(biāo)注,否則的話是會(huì)報(bào)錯(cuò)的。

        deftest():

        n=0

        deft():

        n+=5

        returnn

        returnt

        比如這樣的話,就會(huì)報(bào)錯(cuò):

        閉包當(dāng)中不能使用循環(huán)變量

        閉包有一個(gè)很大的問(wèn)題就是不能使用循環(huán)變量,這個(gè)坑藏得很深,因?yàn)閱渭儚拇a的邏輯上來(lái)看是發(fā)現(xiàn)不了的。也就是說(shuō)邏輯上沒(méi)問(wèn)題的代碼,運(yùn)行的時(shí)候往往會(huì)出乎我們的意料,這需要我們對(duì)底層的原理有深刻地了解才能發(fā)現(xiàn),比如我們來(lái)看一個(gè)例子:

        deftest(x):

        fs=[]

        foriinrange(3):

        deff():

        returnx+i

        fs.append(f)

        returnfs

        fs=test(3)

        forfinfs:

        print(f())

        在上面這個(gè)例子當(dāng)中,我們使用了for循環(huán)來(lái)創(chuàng)建了3個(gè)閉包,我們使用fs存儲(chǔ)這三個(gè)閉包并進(jìn)行返回。然后我們通過(guò)調(diào)用test,來(lái)獲得了這3個(gè)閉包,然后我們進(jìn)行了調(diào)用。

        這個(gè)邏輯看起來(lái)應(yīng)該沒(méi)有問(wèn)題,按照道理,這3個(gè)閉包是通過(guò)for循環(huán)創(chuàng)建的,并且在閉包當(dāng)中我們用到了循環(huán)變量i。那按照我們的想法,最終輸出的結(jié)果應(yīng)該是[3,4,5],但是很遺憾,最后我們得到的結(jié)果是[5,5,5]。

        看起來(lái)很奇怪吧,其實(shí)一點(diǎn)也不奇怪,因?yàn)檠h(huán)變量i并不是在創(chuàng)建閉包的時(shí)候就set好的。而是當(dāng)我們執(zhí)行閉包的時(shí)候,我們?cè)偃ふ疫@個(gè)i對(duì)應(yīng)的取值,顯然當(dāng)我們運(yùn)行閉包的時(shí)候,循環(huán)已經(jīng)執(zhí)行完了,此時(shí)的i停在了2。所以這3個(gè)閉包的執(zhí)行結(jié)果都是2+3也就是5。這個(gè)坑是由Python解釋器當(dāng)中對(duì)于閉包執(zhí)行的邏輯導(dǎo)致的,我們編寫(xiě)的邏輯是對(duì)的,但是它并不按照我們的邏輯來(lái),所以這一點(diǎn)要千萬(wàn)注意,如果忘記了,想要通過(guò)debug查找出來(lái)會(huì)很難。

        總結(jié)雖然從表面上閉包存在一些問(wèn)題和坑點(diǎn),但是它依然是我們經(jīng)常使用的Python高級(jí)特性,并且它也是很多其他高級(jí)用法的基礎(chǔ)。所以我們理解和學(xué)會(huì)閉包是非常有必要的,千萬(wàn)不能因噎廢食。

        其實(shí)并不只是閉包,很多高度抽象的特性都或多或少的有這樣的問(wèn)題。因?yàn)楫?dāng)我們進(jìn)行抽象的時(shí)候,我們固然簡(jiǎn)化了代碼,增加了靈活度,但與此同時(shí)我們也讓學(xué)習(xí)曲線變得陡峭,帶來(lái)了更多我們需要理解和記住的內(nèi)容。本質(zhì)上這也是一個(gè)trade-off,好用的特性需要付出代碼,易學(xué)易用的往往意味著比較死板不夠靈活。對(duì)于這個(gè)問(wèn)題,我們需要保持心態(tài),不過(guò)好在初看時(shí)也許有些難以理解,但總體來(lái)說(shuō)閉包還是比較簡(jiǎn)單的,我相信對(duì)你們來(lái)說(shuō)一定不成問(wèn)題。

        以上內(nèi)容為大家介紹了Python的函數(shù)式編程與閉包,希望對(duì)大家有所幫助,如果想要了解更多Python相關(guān)知識(shí),請(qǐng)關(guān)注IT培訓(xùn)機(jī)構(gòu):千鋒教育。

        聲明:本站稿件版權(quán)均屬千鋒教育所有,未經(jīng)許可不得擅自轉(zhuǎn)載。
        10年以上業(yè)內(nèi)強(qiáng)師集結(jié),手把手帶你蛻變精英
        請(qǐng)您保持通訊暢通,專屬學(xué)習(xí)老師24小時(shí)內(nèi)將與您1V1溝通
        免費(fèi)領(lǐng)取
        今日已有369人領(lǐng)取成功
        劉同學(xué) 138****2860 剛剛成功領(lǐng)取
        王同學(xué) 131****2015 剛剛成功領(lǐng)取
        張同學(xué) 133****4652 剛剛成功領(lǐng)取
        李同學(xué) 135****8607 剛剛成功領(lǐng)取
        楊同學(xué) 132****5667 剛剛成功領(lǐng)取
        岳同學(xué) 134****6652 剛剛成功領(lǐng)取
        梁同學(xué) 157****2950 剛剛成功領(lǐng)取
        劉同學(xué) 189****1015 剛剛成功領(lǐng)取
        張同學(xué) 155****4678 剛剛成功領(lǐng)取
        鄒同學(xué) 139****2907 剛剛成功領(lǐng)取
        董同學(xué) 138****2867 剛剛成功領(lǐng)取
        周同學(xué) 136****3602 剛剛成功領(lǐng)取
        相關(guān)推薦HOT
        python內(nèi)置函數(shù):map、reduce、filter的用法和區(qū)別

        map:根據(jù)函數(shù)對(duì)指定序列做映射map參數(shù)接收兩個(gè)參數(shù):一個(gè)是函數(shù),一個(gè)是序列(可迭代對(duì)象)返回值Python2返回列表python3返回迭代器#例子:#abs()...詳情>>

        2023-11-07 16:59:10
        python虛擬環(huán)境工具virtualenv

        virtualenv是一個(gè)創(chuàng)建隔絕的Python環(huán)境的工具。virtualenv創(chuàng)建一個(gè)包含所有必要的可執(zhí)行文件的文件夾,用來(lái)使用Python工程所需的包。安裝pipins...詳情>>

        2023-11-07 16:30:22
        python函數(shù)調(diào)用和參數(shù)傳遞

        定義過(guò)函數(shù)后,就可以在后面程序中使用這一函數(shù)printsquare_sum(3,4)python通過(guò)位置,知道3對(duì)應(yīng)的是函數(shù)定義中的第一個(gè)參數(shù)a,4對(duì)應(yīng)第二個(gè)參數(shù)b...詳情>>

        2023-11-07 16:19:34
        python字節(jié)碼和機(jī)器碼的區(qū)別

        機(jī)器碼,學(xué)名機(jī)器語(yǔ)言指令,有時(shí)也被稱為原生碼,是電腦的CPU可直接解讀的數(shù)據(jù)。字節(jié)碼是一種中間狀態(tài)(中間碼)的二進(jìn)制代碼(文件)。需要直譯器...詳情>>

        2023-11-07 16:05:10
        Python常用切片操作

        以列表:a=[0,1,2,3,4,5,6,7,8,9]為說(shuō)明對(duì)象1.取偶數(shù)位置>>>b=a[::2][0,2,4,6,8]2.取奇數(shù)位置>>>b=a[1::2][1,3,5,7,9]3.拷貝整個(gè)對(duì)象>...詳情>>

        2023-11-07 15:21:58
        快速通道
        天等县| 峨山| 兴安盟| 霞浦县| 怀宁县| 布尔津县| 北流市| 乌拉特前旗| 漳浦县| 米易县| 客服| 古交市| 孝昌县| 嘉禾县| 铁岭市| 安平县| 贵定县| 龙南县| 招远市| 个旧市| 修文县| 武平县| 游戏| 衡阳县| 泊头市| 大洼县| 瑞安市| 常山县| 郧西县| 平南县| 宜州市| 文登市| 虞城县| 鄂托克前旗| 尤溪县| 翼城县| 汉中市| 温泉县| 仙居县| 团风县| 永修县|