溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Python函數(shù)嵌套-作用域-閉包-LEGB-函數(shù)銷毀

發(fā)布時間:2020-07-12 22:26:34 來源:網(wǎng)絡(luò) 閱讀:335 作者:Python熱愛者 欄目:編程語言

1 函數(shù)嵌套

一個函數(shù)中存在另外一個函數(shù)(定義/調(diào)用),這種方式我們稱之為函數(shù)嵌套。所以:函數(shù)的嵌套主要分為嵌套調(diào)用,以及嵌套定義。


Python函數(shù)嵌套-作用域-閉包-LEGB-函數(shù)銷毀

注意:在函數(shù)的內(nèi)部定義函數(shù),只能在函數(shù)內(nèi)部進(jìn)行調(diào)用,在其他地方是無法進(jìn)行調(diào)用,強(qiáng)行調(diào)用就會提示NameError異常,所以說函數(shù)是有可見范圍的,這就涉及到了作用域了

2 作用域

一個標(biāo)識符的可見范圍,叫做標(biāo)識符的作用域。一般常說的是變量的作用域。根據(jù)作用的范圍主要分為全局作用域和局部作用域。

全局作用域:在整個程序運(yùn)行環(huán)境中都可見

局部作用域:在函數(shù)、類的內(nèi)部可見,并且使用范圍不能超過所在的局部作用域(比如在函數(shù)內(nèi)部定義了一個變量x,我在全局使用變量x是不行的。)


Python函數(shù)嵌套-作用域-閉包-LEGB-函數(shù)銷毀

全局變量x在全局生效,所以內(nèi)部函數(shù)inner是可以打印x的

局部變量y只在inner內(nèi)部生效,所以在全局print(y) 是無法調(diào)用局部變量y的

觀察下面的例子:


Python函數(shù)嵌套-作用域-閉包-LEGB-函數(shù)銷毀

代碼是從上到下執(zhí)行的,所欲這樣寫也沒什么毛病,但是這里這個例子是無法執(zhí)行的,為什么呢?

x作為全局變量,在inner內(nèi)部是可見的

在定義函數(shù)的階段,Python的函數(shù)是作為一個整體一起被解釋的。

inner函數(shù)在解釋時,解釋器發(fā)現(xiàn)在inner內(nèi)部對x進(jìn)行了定義(x += 1),那么它就不會在調(diào)用全局變量x,而是標(biāo)識x是局部定義的變量

而在執(zhí)行x+=1的時候,inner內(nèi)部的x還沒有被定義,所以會提示x在定義前被執(zhí)行了。(x += 1 --> x = x + 1 ,預(yù)先求 x + 1 時提示的)。

如何解決呢?有兩種方法:更換變量名稱、聲明當(dāng)前變量非本地變量(global)


Python函數(shù)嵌套-作用域-閉包-LEGB-函數(shù)銷毀

2.1 global關(guān)鍵字

我們通過在函數(shù)內(nèi)部使用global關(guān)鍵字來聲明一個變量不是局部變量,而是一個全局變量。


Python函數(shù)嵌套-作用域-閉包-LEGB-函數(shù)銷毀

雖然全局變量x,在全局沒有被定義,但是由于在函數(shù)內(nèi)部使用了global關(guān)鍵字,所以x就變成了全局變量了。使用了global關(guān)鍵字,那么之前的例子就可以進(jìn)行如下修改了


Python函數(shù)嵌套-作用域-閉包-LEGB-函數(shù)銷毀

針對global的總結(jié):

外部作用域變量在內(nèi)部作用域是可見的,但是不要在內(nèi)部函數(shù)中直接使用或者修改,因為函數(shù)的目的就是為了封裝,盡量與外界隔離。

如果函數(shù)需要使用外部全局變量,請盡量使用函數(shù)的形參定義,在調(diào)用時傳遞實參來使用

建議不要使用global。

3 閉包

在很多編程語言中都存在閉包的概念,那什么是閉包呢?閉包其實就是一個概念,出現(xiàn)在嵌套函數(shù)中,指的是:內(nèi)層函數(shù)引用到了外層函數(shù)的自由變量,就形成了閉包

自由變量:未在本地作用域中定義的變量,比如在嵌套函數(shù)的外層定義的變量(非全局變量),對內(nèi)層來說,這個變量就叫做自由變量。


Python函數(shù)嵌套-作用域-閉包-LEGB-函數(shù)銷毀

注意:上面這個例子比較特殊,首先它是一個閉包,在inner函數(shù)內(nèi)引用了外層函數(shù)的自由變量C。因為這里的c是一個引用類型,我們可以直接通過c來操作c中的元素,但是沒辦法對c本身進(jìn)行修改,即c += [1,3]??此剖橇斜砥唇?,但是它會重新對c進(jìn)行聲明,這就引發(fā)了之前的問題,內(nèi)部函數(shù)inner沒有定義c,所以會報錯!所以當(dāng)c不是引用類型的話,我們就沒辦法修改了嗎?當(dāng)然不是,可以使用global把c聲明為全局變量,但是這就不是閉包了,所以這里就需要使用nonlocal了(python 3 特有)。

疑問?我們都說函數(shù)執(zhí)行完畢后,函數(shù)的內(nèi)部變量將會被回收,這里的outer執(zhí)行完畢后,那么變量c應(yīng)該會被回收啊,為什么還能被內(nèi)層的inner找到呢?這是因為在定義階段,解釋器解釋到inner函數(shù)時,由于函數(shù)是作為一個整體被解析的,所以解釋器知道在inner內(nèi)部引用了外部的變量,所以在執(zhí)行函數(shù)outer時,并不會回收已被內(nèi)部函數(shù)inner引用的自由變量c。

3.1 nonlocal關(guān)鍵字

使用了nonlocal關(guān)鍵字,將變量標(biāo)記為不在本地作用域定義,而在上一級局部作用域中定義,但不能是全局作用域中定義。

nonlocal只能用在嵌套函數(shù)的內(nèi)部


Python函數(shù)嵌套-作用域-閉包-LEGB-函數(shù)銷毀

4 默認(rèn)值的作用域

在Python中,一切皆對象,函數(shù)也不列外,當(dāng)我們給函數(shù)定義默認(rèn)值時,Python會把它存放在函數(shù)的屬性中,這個屬性值就伴隨這個函數(shù)對象的整個生命周期。

foo.__defaults__屬性查看函數(shù)的默認(rèn)值屬性


Python函數(shù)嵌套-作用域-閉包-LEGB-函數(shù)銷毀

仔細(xì)查看輸出結(jié)果,發(fā)現(xiàn)函數(shù)地址沒有變,也就是說函數(shù)這個對象沒有變,但是我們發(fā)現(xiàn)每次它的__default__屬性都會發(fā)生變化,這是為什么呢?這是因為sed和list的默認(rèn)值都是引用類型,它們引用的都是函數(shù)在定義時定義的默認(rèn)值中。 雖然函數(shù)執(zhí)行完就釋放了內(nèi)存空間,也是由于引用類型,指向默認(rèn)空間的指針沒了,但是已經(jīng)在調(diào)用時改變了默認(rèn)值空間的對象中的元素,所以在下一次再次調(diào)用時此時默認(rèn)值空間的元素已經(jīng)被改變了。所以當(dāng)函數(shù)的默認(rèn)值為引用類型時,這點(diǎn)要特別的注意了

解決辦法:

在定義時使用引用類型時,在函數(shù)內(nèi)部使用前先進(jìn)行copy。

在定義函數(shù)時默認(rèn)使用None值,在函數(shù)內(nèi)判斷如果是None則開辟一個引用類型。

5 變量名解析原則LEGB

變量的解析原則,也可以理解為變量的查找順序:

L(Local): 本地作用域、局部作用域的local命名空間。函數(shù)調(diào)用是創(chuàng)建,調(diào)用結(jié)束消亡

E(Enclosing): Python 2.2時引入嵌套函數(shù),實現(xiàn)了閉包,這個就是嵌套函數(shù)的外部函數(shù)的命名空間

G(Global): 全局作用域,即一個模塊的命名空間。模塊被import時創(chuàng)建,解釋器退出時消亡

B(Build-in): 內(nèi)置模塊的命名空間,生命周期從Python解釋器啟動時創(chuàng)建到解釋器退出時消亡。例如print函數(shù)、open函數(shù)等。

變量查找的規(guī)則為 L > E > G > B,即:先本地后嵌套再全局最后是內(nèi)置函數(shù)中

6 函數(shù)的銷毀

全局函數(shù):

重新定義同名函數(shù)

del 語句刪除函數(shù)名稱,函數(shù)對象引用計數(shù)減1

程序結(jié)束時

局部函數(shù):

重新在上級作用域定義同名函數(shù)

del 語句刪除函數(shù)名稱,函數(shù)對象的引用計數(shù)減1

上級作用域銷毀時


向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI