小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

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

 xiaoyimin 2019-05-05

1 函數(shù)嵌套

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

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

2 作用域

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

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

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

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

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

觀察下面的例子:

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

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

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

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

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

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

2.1 global關(guān)鍵字

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

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

針對global的總結(jié):

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

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

建議不要使用global。

3 閉包

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

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

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

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

3.1 nonlocal關(guān)鍵字

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

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

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

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

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

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

解決辦法:

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

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

5 變量名解析原則LEGB

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

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

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

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

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

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

6 函數(shù)的銷毀

全局函數(shù):

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

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

程序結(jié)束時(shí)

局部函數(shù):

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

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

上級作用域銷毀時(shí)

    本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多