第十七章、作用域

Python作用域基礎(chǔ)

當(dāng)你在一個(gè)程序中適用變量名時(shí),Python創(chuàng)建、改變或查找變量名都是在所謂的命名空間(一個(gè)保存變量名的地方)中進(jìn)行的。當(dāng)我們談?wù)摰剿阉髯兞棵麑?duì)應(yīng)于代碼的值的時(shí)候,作用域這個(gè)術(shù)語指的就是命名空間。

關(guān)于所有變量名,包括作用域的定義在內(nèi),都是Python賦值的時(shí)候生成的。

在默認(rèn)的情況下,一個(gè)函數(shù)的所有變量名都是與函數(shù)的命名空間相關(guān)聯(lián)的。這意味著:

①、一個(gè)在def內(nèi)定義的變量名能夠被def內(nèi)的代碼使用。不能在函數(shù)的外部引用這樣的變量名。

②、def之中的變量名與def之外的變量名并不沖突,即使是使用在別處的相同的變量名。

變量可以在3個(gè)不同的地方分配,分別對(duì)應(yīng)3個(gè)不同的作用域:

①、如果一個(gè)變量在def內(nèi)賦值,它被定位在這個(gè)函數(shù)之內(nèi)。

②、如果一個(gè)變量在一個(gè)嵌套的def中賦值,對(duì)于嵌套的函數(shù)來說,它是非本地的。

③、如果在def之外賦值,它就是整個(gè)文件全局的。

1、作用域法則:

①、內(nèi)嵌的模塊是全局作用域。

②、全局作用域的作用范圍僅限于單個(gè)文件。

③、每次對(duì)函數(shù)的調(diào)用都創(chuàng)建了一個(gè)新的本地作用域。

④、賦值的變量名除非聲明為全局變量或非本地變量,否則均為本地變量。

⑤、所有其他的變量名都可以歸納為本地、全局或者內(nèi)置的。

2、變量名解析:LEGB原則:

對(duì)于一個(gè)def語句:

①、變量名引用分為三個(gè)作用域進(jìn)行查找:首先是本地,之后是函數(shù)內(nèi)(如果有的話),之后是全局,最后是內(nèi)置。

②、在默認(rèn)情況下,變量名賦值會(huì)創(chuàng)建或者改變本地變量。

③、全局聲明和非本地聲明將賦值的變量名映射到模塊文件內(nèi)部的作用域。

Python的變量名解析機(jī)制有時(shí)稱為LEGB法則,這也是由作用域的命令而來的。

①、當(dāng)在函數(shù)中使用未認(rèn)證的變量名時(shí),Python搜索4個(gè)作用域【本地作用域(L),之后是上一層結(jié)構(gòu)中def或者lambda的本地作用域(E),之后是全局作用域(G)),最后是內(nèi)置作用域(B)】并且在第一處能夠找到這個(gè)變量名的地方停下來。如果變量名在這次搜索中沒有找到,Python會(huì)報(bào)錯(cuò)。

②、當(dāng)在函數(shù)中給一個(gè)變量名賦值時(shí)(而不是在一個(gè)表達(dá)式中對(duì)其進(jìn)行引用),Python總是創(chuàng)建或改變本地作用域的變量名,除非它已經(jīng)在那個(gè)函數(shù)中聲明為全局變量。

③、當(dāng)在函數(shù)之外給一個(gè)變量名賦值時(shí)(也就是,在一個(gè)模塊文件的頂層,或者是在交互提示模式下),本地作用域與全局作用域(這個(gè)模塊的命名空間)是相同的。

3、作用域?qū)嵗?/h4>

4、內(nèi)置作用域:

5、global語句:

①、全局變量是位于模塊文件內(nèi)部的頂層的變量名。

②、全局變量如果是再函數(shù)內(nèi)被賦值的話,必須經(jīng)過聲明。

③、全局變量名在函數(shù)的內(nèi)部不經(jīng)過聲明也可以被引用。

6、最小化全局變量:

7、最小化文件間的修改:

8、其他訪問全局變量的方法:

作用域和嵌套函數(shù)

1、嵌套作用域的細(xì)節(jié):

對(duì)于一個(gè)函數(shù):

①、一個(gè)引用(X)首先在本地(函數(shù)內(nèi))作用域查找變量名X;之后會(huì)在代碼的語法上嵌套了的函數(shù)中的本地作用域,從內(nèi)到外查找;之后查找當(dāng)前的全局作用域(模塊文件);最后再內(nèi)置作用域內(nèi)(模塊__builtin__)。全局聲明將會(huì)直接從全局(模塊文件)作用域進(jìn)行搜索。

②、在默認(rèn)情況下,一個(gè)賦值(X=value)創(chuàng)建或改變了變量名X的當(dāng)前作用域。

2、嵌套作用域舉例:

①、工廠函數(shù):

根據(jù)要求的對(duì)象,這種行為有時(shí)也叫做閉合(closure)或者工廠函數(shù)——一個(gè)能夠記住嵌套作用域的變量值的函數(shù),盡管那個(gè)作用域或許已經(jīng)不存在了。

②、使用默認(rèn)參數(shù)來保留嵌套作用域的狀態(tài):

③、嵌套作用域和lambda:

像def一樣,lambda表達(dá)式引入了新的本地作用域。

④、作用域與帶有循環(huán)變量的默認(rèn)參數(shù)相比較:

在已給出的法則中有個(gè)值得注意的特例:如果lambda或者def在函數(shù)中定義,嵌套在一個(gè)循環(huán)之中,并且嵌套的函數(shù)引用了一個(gè)上層作用域的變量,該變量被循環(huán)所改變,所有在這個(gè)循環(huán)中產(chǎn)生的函數(shù)將會(huì)有相同的值——在最后一次完成時(shí)被引用變量的值。

⑤、任意作用域的嵌套:

nonlocal語句

1、nonlocal基礎(chǔ):

①、global使得作用域查找從嵌套的模塊的作用域開始,并且允許對(duì)那里的名稱賦值。如果名稱不存在于該模塊,作用域查找繼續(xù)到內(nèi)置作用域,但是,對(duì)全局名稱的賦值總是在模塊的作用域中創(chuàng)建或修改它們。

②、nonlocal限制作用域查找知識(shí)嵌套的def,要求名稱已經(jīng)存在于那里,并且允許對(duì)它們賦值。作用域查找不會(huì)繼續(xù)到全局或內(nèi)置作用域。

2、nonlocal應(yīng)用:

①、使用nonlocal進(jìn)行修改:

②、邊界情況:

首先,和global語句不通,當(dāng)執(zhí)行一條nonlocal語句時(shí),nonlocal名稱必須已經(jīng)在一個(gè)嵌套的def作用域中賦值過,否則將會(huì)得到一個(gè)錯(cuò)誤——不能通過在嵌套的作用域中賦給它們一個(gè)新值來創(chuàng)建它們。

其次,nonlocal限制作用域查找僅為嵌套的def,nonlocal不會(huì)在嵌套的模塊的全局作用域或所有def之外的內(nèi)置作用域中查找,即便已經(jīng)有了這些作用域。

3、為什么使用nonlocal:

①、與全局共享狀態(tài):

在Python2.6中實(shí)現(xiàn)nonlocal效果的一種通用方法也是較早的方法,就是直接把狀態(tài)移出全局作用域(嵌套的模塊)。

②、使用類的狀態(tài)(預(yù)覽):

Python2.6中針對(duì)可改變信息的另一種較早的方法是使用帶有屬性的類,從而讓狀態(tài)信息的訪問比隱式的范圍查找規(guī)則更明確。

③、使用函數(shù)屬性的狀態(tài):

我們有時(shí)候可以使用函數(shù)屬性實(shí)現(xiàn)與nonlocal相同的效果——用戶定義的名稱直接附加給函數(shù)。

本章小結(jié)

這一章,我們學(xué)習(xí)了關(guān)于函數(shù)的兩個(gè)關(guān)鍵概念:作用域(當(dāng)使用時(shí)變量如何查找)。正如我們所學(xué)的那樣,變量作為它所賦值的函數(shù)定義的本地變量,除非它們特定地聲明為全局變量或非本地變量。我們也學(xué)過了一些更高級(jí)的作用域概念,包括嵌套函數(shù)作用域和函數(shù)屬性。最后,我們學(xué)習(xí)了一些通用的設(shè)計(jì)觀點(diǎn)(避免使用全局變量和跨文件間的修改)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容