<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Python入門系列26 - 進(jìn)階必修之閉包(一)

          共 4875字,需瀏覽 10分鐘

           ·

          2019-11-04 23:20

          Python入門系列26

          d10ebfe122f3bf23fa14f10e4e3daba8.webp

          進(jìn)階必修之閉包(一)


          本篇閱讀時(shí)間約為8分鐘。


          1

          前言



          從本章起,開(kāi)始進(jìn)行python進(jìn)階篇的知識(shí)分享,python入門系列0-25皆為基礎(chǔ)知識(shí),其中有兩篇是結(jié)合基礎(chǔ)講解實(shí)戰(zhàn),分別是暴力破解和圖片定位,鏈接如下:
          Python入門系列20 - 5分鐘教你用圖片定位具體地址!

          Python入門系列17 - 30行代碼破解加密ZIP文件


          相信學(xué)到現(xiàn)在,這兩篇實(shí)戰(zhàn)的原理以及代碼你一定可以看懂了!好了,廢話不多說(shuō)了,進(jìn)入今天的正題吧,python進(jìn)階篇-閉包!

          2

          再談函數(shù)


          講真的,閉包從概念講是非常難講明白的一個(gè)詞。所以,先來(lái)搞清楚函數(shù)在Python里是怎樣的存在,深入了解了函數(shù)的概念,會(huì)為學(xué)習(xí)閉包打下基礎(chǔ)。閉包與函數(shù)有著密不可分的關(guān)系。


          通常,在一般的編程語(yǔ)言中,函數(shù)只是一段可執(zhí)行的邏輯代碼,并不是所謂的對(duì)象。比如在Java中,如下代碼,打印輸出一句話:


          /**
          *?函數(shù)描述:說(shuō)一句話
          *?public?:?共有方法
          * void ??:沒(méi)有返回類型
          *?
          @param?words?參數(shù),傳入的話語(yǔ)
          */
          public?static?void?say(String?words)?{
          ? ? System.out.println(words);
          }
          public?static?void?main(String[]?args)?{
          ? ? say(
          "你好");
          }


          f604865ca9b495edf50cbc77e09ede9a.webp

          像Java這種有明確類型定義聲明的語(yǔ)言,如果沒(méi)有設(shè)置返回類型,在調(diào)用函數(shù)時(shí),則不能用任何變量去接受,否則編譯時(shí)就會(huì)報(bào)錯(cuò)。(只是作對(duì)比的例子,非專業(yè)人員忽略即可)。


          再來(lái)看下Python中呢,若我們像打印一句話,也需要定義一個(gè)函數(shù),然后調(diào)用即可,同時(shí),我們可以通過(guò)任何變量來(lái)將此函數(shù)進(jìn)行賦值操作,如下:


          #?打印一句話
          def?say(words):
          ????print(words)


          say('你好')
          a?
          =?say
          print(type(a))


          4bd64d4b613028e0f97295d7f2b1e8fe.webp

          結(jié)果顯示,在Python中,是可以用函數(shù)不加小括號(hào)的形式將其賦值給任何變量,并且其自身也可以作為函數(shù)的參數(shù)進(jìn)行傳遞,不僅如此,函數(shù)也可以用此種方式將其自身作為返回結(jié)果進(jìn)行返回,通過(guò)上面的代碼可以看到,say賦值給變量a,打印a的類型,得到class?function,說(shuō)明它是一個(gè)。之前的小課堂中提及過(guò),在Python中一切皆為對(duì)象,函數(shù)也不例外!


          可能有人會(huì)說(shuō),你在Java中不是這么寫(xiě)的啊,你也像在Python中那樣調(diào)用試下,看看結(jié)果唄,于是有了下面的圖:


          b7cdda5d32abf1faa78c48e126982ece.webp

          可以看到,當(dāng)在Java中寫(xiě)say不加小括號(hào)時(shí),編譯器已經(jīng)報(bào)錯(cuò)了,提示找不到say的標(biāo)識(shí)符(會(huì)Java的人都知道這么寫(xiě)是沒(méi)有意義的)。這也是Java與Python作為編程語(yǔ)言的不同之處,一個(gè)為編譯型語(yǔ)言,一個(gè)為解釋型語(yǔ)言。


          正因?yàn)镻ython有了這么一個(gè)特性,所以才會(huì)很好地支持接下來(lái)要介紹的閉包!


          3

          什么是閉包?


          在解釋概念之前,先來(lái)看個(gè)自帶場(chǎng)景的小例子吧!不知道大家還記不記得小學(xué)(是小學(xué)還是初中來(lái)著,忘記了...默認(rèn)小學(xué)吧?。W(xué)過(guò)的一個(gè)數(shù)學(xué)公式,如何去求圓的面積?記憶好的一定記得:85f2902ac49d1bd7cfe58640d10cfe5c.webp,r為圓的半徑,π為3.1415926.....


          現(xiàn)在的場(chǎng)景是,需要定義一個(gè)求圓形面積的函數(shù),同時(shí),在這個(gè)函數(shù)的外部還要包裹著一層求圓形面積之前提前做準(zhǔn)備的外層函數(shù),這外層函數(shù)的目的是你可以在真正求圓形面積前演算一些內(nèi)容(假設(shè)大家都是剛學(xué)這個(gè)公式的小學(xué)生喲!03f09d262030a3419053952c24c89e70.webp),寫(xiě)法分解成以下步驟:


          1. 定義兩個(gè)函數(shù),并且在調(diào)用內(nèi)部計(jì)算圓形面積的函數(shù)


          def?circular_area_pre():
          ????def?circular_area():
          ????????print('This?is?circular_area?function!')


          其中 circular_area_pre 是計(jì)算圓形面積前的準(zhǔn)備函數(shù),circular_area 是計(jì)算圓形面積的函數(shù)。此時(shí)若想在外面直接調(diào)用?circular_area 如何做呢?(不要感到這種寫(xiě)法奇怪,Python中是可以進(jìn)行函數(shù)嵌套的?。﹪L試下自己手動(dòng)調(diào)用!如下圖:


          65704257b058ee9b2520f3eb9bf5c707.webp


          當(dāng)嘗試在外層直接進(jìn)行調(diào)用時(shí),可以看到,已經(jīng)報(bào)錯(cuò)了!如何正確調(diào)用呢?在上面的標(biāo)題「再談函數(shù)」中說(shuō)過(guò),函數(shù)可以通過(guò)“對(duì)象”的寫(xiě)法將其自身作為結(jié)果進(jìn)行返回!(忘記的往上翻翻,找找看!)所以改下寫(xiě)法如下:


          def?circular_area_pre():
          ????def?circular_area():
          ????????print('This?is?circular_area?function!')

          ????return?circular_area()

          c?
          =?circular_area_pre()
          c()


          通過(guò)調(diào)用外層的計(jì)算準(zhǔn)備函數(shù) circular_area_pre ,函數(shù)返回接受到的內(nèi)部計(jì)算圓形面積函數(shù) circular_area 作為變量c,以函數(shù)的形式調(diào)用變量c(也就是在c后面加上小括號(hào)進(jìn)行函數(shù)調(diào)用),執(zhí)行即可!而此時(shí)的變量c實(shí)際上是一個(gè)函數(shù)(這點(diǎn)在后面的步驟中會(huì)進(jìn)行驗(yàn)證,先記住。)!執(zhí)行一下,咦???發(fā)現(xiàn)報(bào)錯(cuò)了,因?yàn)槎嗔藗€(gè)小括號(hào):

          acd9bb34c320c2a0ce6945272173e609.webp


          去掉后成功,所以一定要注意小括號(hào)的問(wèn)題?。∪缦聢D :

          2e64a44e0ca2babad0544f214f81d6ba.webp


          2. 在第一步的基礎(chǔ)上,將其補(bǔ)充完整,套入公式


          def?circular_area_pre():
          ????"""?省略了一些演算步驟的代碼,畢竟假設(shè)嘛?"""
          ????pai?
          =?3.14

          ????def?circular_area(r):
          ????????s?
          =?pai?*?r?**?2
          ????????return?s

          ????return?circular_area

          c?=?circular_area_pre()
          print(c(10))


          解釋下這段代碼的含義,將pai(π)定義為3.14,放在作為計(jì)算圓形面積之前的函數(shù) circular_area_pre?中,而求面積的公式則寫(xiě)在 circular_area 函數(shù)中,將面積變量s作為內(nèi)部函數(shù)返回,同時(shí),最外層的?circular_area_pre 函數(shù)返回?circular_area 函數(shù)作為結(jié)果。想要計(jì)算出圓形的面積,在外層就需要先調(diào)用?circular_area_pre 準(zhǔn)備函數(shù),在調(diào)用其返回結(jié)果變量c,打印 c(10),可以看到如下圖結(jié)果:


          5510f2a58ebe9a82b56b1345bdfe2167.webp


          打印結(jié)果輸出的是314.0,實(shí)際上就是將10傳入到了?circular_area 函數(shù)中,而其中的pai引用的是在?circular_area_pre 局部里定義的pai,值為3.14 。驗(yàn)證下步驟1說(shuō)的,看下變量c的類型,以及直接打印c會(huì)出現(xiàn)什么樣子的結(jié)果:


          10bcb15c81f0235d4d075ca98edb305b.webp


          現(xiàn)在可以看到,其實(shí)變量c就是一個(gè)類,而它的類型是function.?


          3. 關(guān)于 pai 的定義位置


          拋開(kāi)外層的準(zhǔn)備函數(shù),假設(shè)現(xiàn)在只有計(jì)算圓形的函數(shù),假設(shè)阿基米德突然復(fù)活了,把這個(gè)pai推算成了其它數(shù)字!姑且定義為10吧,如下:


          pai?=?3.14

          def?circular_area(r):
          ????s?
          =?pai?*?r?**?2
          ????return?s
          pai?=?10
          print(circular_area(10))


          b5b6bbda23891b301444aa58e0ebdbc0.webp

          輸出結(jié)果為1000,記住這個(gè)值,咱們繼續(xù)往下看!


          4. 如果此時(shí)在外部修改了pai的值,打印結(jié)果如何?


          回到雙層函數(shù)的示例來(lái),在?circular_area 函數(shù)中,pai是沒(méi)有被定義的,所以它會(huì)向上一層尋找,于是找到了?circular_area_pre 函數(shù)中定義的pai,所以計(jì)算的時(shí)候值就會(huì)取為3.14,假設(shè)現(xiàn)在依然在最外層函數(shù)的外面修改pai的值,繼續(xù)改為10,那么代碼的結(jié)果會(huì)是如何呢?


          4162cca93ed7cd60b743267d1f94302a.webp

          來(lái)看下:


          def?circular_area_pre():
          ????"""?省略了一些演算步驟的代碼,畢竟假設(shè)嘛?"""
          ????pai?
          =?3.14

          ????def?circular_area(r):
          ????????s?
          =?pai?*?r?**?2
          ????????return?s

          ????return?circular_area


          pai?=?10
          c?=?circular_area_pre()
          print(c(10))


          在調(diào)用準(zhǔn)備函數(shù)之前,將pai修改為10!猜猜看,結(jié)果會(huì)打印出什么呢?(先不要看結(jié)果,自己思考一下!)結(jié)果如下:


          3df7de1c3605ff107c15280f9ab2fc49.webp


          沒(méi)錯(cuò),你沒(méi)有看錯(cuò),依然是314.0,這個(gè)結(jié)果與沒(méi)加入pai = 10 的代碼得到的結(jié)果是一樣的!為什么不是1000呢???請(qǐng)繼續(xù)往后看!


          5. 閉包的概念


          啰里啰嗦的舉例了這么多,到底跟今天要說(shuō)的閉包有什么關(guān)系呢!各位看官,莫急,下面就是重頭戲了,只要你耐心的把上面的示例都看懂,保證你看完接下來(lái)的這段概念性文字一目了之!


          在上面的函數(shù)?circular_area 與 pai = 3.14 形成了閉包!通俗的說(shuō)就是把內(nèi)部函數(shù) circular_area 與?pai = 3.14 這個(gè)環(huán)境變量包含在了一起,做了一個(gè)封閉,所以外界想要去改變 pai 這個(gè)變量是改變不了的!


          需要注意的是,所謂的環(huán)境變量,一定要定義在內(nèi)部函數(shù)的外部,就像 pai 一樣,且不能是全局變量!


          閉包的概念:閉包 = 函數(shù) + 環(huán)境變量!


          6. Python函數(shù)作用域


          Python變量的作用域一共有4種,分別是:

          • L (Local) 局部作用域

          • E (Enclosing) 閉包函數(shù)外的函數(shù)中

          • G (Global) 全局作用域

          • B (Built-in) 內(nèi)建作用域

          以 L –> E –> G –>B 的規(guī)則查找,即:在局部找不到,便會(huì)去局部外的局部找(例如閉包),再找不到就會(huì)去全局找,再者去內(nèi)建中找。


          通過(guò)這段作用域的概念,可以得知,為什么在求面積的函數(shù)外部去修改pai的值,最終取到的還是原來(lái)的3.14,Python的變量查詢是一個(gè)“由內(nèi)到外”的過(guò)程,一旦找到,則取最內(nèi)部的變量進(jìn)行使用。


          4

          如何查看一個(gè)函數(shù)是否閉包


          閉包是可以通過(guò)python內(nèi)置函數(shù)檢測(cè)出來(lái)的,如下:


          def?circular_area_pre():
          ????"""?省略了一些演算步驟的代碼,畢竟假設(shè)嘛?"""
          ????pai?
          =?3.14

          ????def?circular_area(r):
          ????????s?
          =?pai?*?r?**?2
          ????????return?s

          ????return?circular_area

          pai?=?10
          c?=?circular_area_pre()
          print(c.__closure__)
          print(c.__closure__[0].cell_contents)


          5bbc37795076c2078bc265c58cc80060.webp


          通過(guò)調(diào)用 __closure__ 內(nèi)置方法可以查看到兩個(gè)內(nèi)存地址,結(jié)果返回cell就是閉包,None 則不是閉包,可以看出來(lái)其實(shí)這是一個(gè)元組類型,使用[0].cell_contents可以得到閉合數(shù)值,也就閉包所需要的環(huán)境變量。


          小題,請(qǐng)你判斷下面這段代碼屬于閉包嗎?如下:


          def?circular_area_pre():
          ????"""?省略了一些演算步驟的代碼,畢竟假設(shè)嘛?"""

          ????def?circular_area(r):
          ????????pai?
          =?3.14
          ????????s?=?pai?*?r?**?2
          ????????return?s

          ????return?circular_area


          c?=?circular_area_pre()
          print(c.__closure__)
          print(c.__closure__[0].cell_contents)




          答案:

          55057b6be5545553c440c5637e2563a1.webp


          只把pai 進(jìn)行了換位,導(dǎo)致現(xiàn)在的寫(xiě)法并不是閉包,調(diào)用Python內(nèi)置方法來(lái)查看,得到的是None,沒(méi)獲取到閉包時(shí)產(chǎn)生的cell!


          5

          總結(jié)


          說(shuō)真的,本章的閉包要想講明白真的挺難得,這篇文章大概是花了一星期的時(shí)間去寫(xiě)的,基本上基礎(chǔ)的概念介紹的差不多了,但是要想懂閉包,還是得看懂示例才行,記住閉包就是函數(shù)和環(huán)境變量封閉組成的!外界想改環(huán)境變量?沒(méi)門!


          后續(xù)還有一篇閉包二,會(huì)講述閉包的使用場(chǎng)景,究竟什么時(shí)候使用閉包才是合適的。敬請(qǐng)期待.....


          至此完!

          瀏覽 63
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  少妇人妻一级A毛片 | 丁香五月婷婷在线观看 | 盗摄—AV国产盗摄 | 啪啪啪视频在线观看 | 黑人精品XXX一区一二区 |