Python NameSpace(命名空間)

這篇主要是偏理論的文章曙咽,可能會(huì)讀著有些乏味,但是在開發(fā)過程中涤浇,卻有著舉足輕重的地位鳖藕,很多無法排出的BUG很有可能就與此相關(guān)。

1只锭、概述

A namespace is a mapping from names to objects.Most namespaces are currently implemented as Python dictionaries著恩。
命名空間是名字和對(duì)象的映射,命名空間是通過 Python Dictionary(字典) 來實(shí)現(xiàn)的蜻展。

  • 命名空間提供了一個(gè)在大型項(xiàng)目下避免名字沖突的方法

  • Python 中各個(gè)命名空間都是獨(dú)立的喉誊,他們之間無任何關(guān)系

  • 一個(gè)命名空間中不能有重名,但不同的命名空間是可以重名而沒有任何影響纵顾。

命名空間就像是計(jì)算機(jī)中的文件夾一樣伍茄,同一個(gè)文件夾中的文件不可重名,但是如果兩個(gè)文件從屬于不同的文件夾就可以重名施逾。
image

同理相同的對(duì)象名可以存在不同的命名空間中:

image

2敷矫、命名空間種類

命名空間的種類分為 3 類,命名空間的種類也體現(xiàn)了命名空間的生命周期汉额。三個(gè)種類及生命周期描述如下:

1)內(nèi)置名稱(built-in names)

Python 語言內(nèi)置的名稱曹仗,比如函數(shù)名 abs、char 和異常名稱 BaseException蠕搜、Exception 等等怎茫。

生命周期:

對(duì)于Python built-in names組成的命名空間,它在Python解釋器啟動(dòng)的時(shí)候被創(chuàng)建讥脐,在解釋器退出的時(shí)候才被刪除遭居;

2)全局名稱(global names)

模塊中定義的名稱,記錄了模塊的變量旬渠,包括函數(shù)俱萍、類、其它導(dǎo)入的模塊告丢、模塊級(jí)的變量和常量枪蘑。

生命周期:

對(duì)于一個(gè)Python模塊的global namespace损谦,它在這個(gè)module被import的時(shí)候創(chuàng)建,在解釋器退出的時(shí)候退出岳颇;

3)局部名稱(local names)

函數(shù)中定義的名稱照捡,記錄了函數(shù)的變量,包括函數(shù)的參數(shù)和局部定義的變量话侧。(類中定義的也是)

生命周期:

對(duì)于一個(gè)函數(shù)的local namespace栗精,它在函數(shù)每次被調(diào)用的時(shí)候創(chuàng)建,函數(shù)返回的時(shí)候被刪除瞻鹏。

注意:命名空間的生命周期取決于對(duì)象的作用域悲立,如果對(duì)象執(zhí)行完成,則該命名空間的生命周期就結(jié)束新博。因此薪夕,我們無法從外部命名空間訪問內(nèi)部命名空間的對(duì)象。例如:

命名空間分類圖如下:

image

3赫悄、命名空間查找原献、創(chuàng)建、銷毀順序

3.1 查找變量

如果程序執(zhí)行時(shí)去使用一個(gè)變量 hello 埂淮,那么 Python姑隅, 查找變量順序?yàn)椋?/p>

局部的命名空間 -> 全局命名空間 -> 內(nèi)置命名空間

如果按照這個(gè)順序找不到相應(yīng)的變量,它將放棄查找并拋出一個(gè) NameError 異常:

3.2 各命名空間創(chuàng)建順序:

python解釋器啟動(dòng) ->創(chuàng)建內(nèi)建命名空間 -> 加載模塊 -> 創(chuàng)建全局命名空間 ->函數(shù)被調(diào)用 ->創(chuàng)建局部命名空間

3.3 各命名空間銷毀順序:

函數(shù)調(diào)用結(jié)束 -> 銷毀函數(shù)對(duì)應(yīng)的局部命名空間 -> python虛擬機(jī)(解釋器)退出 ->銷毀全局命名空間 ->銷毀內(nèi)建命名空間

4同诫、命名空間總結(jié)

一個(gè)模塊的引入粤策,函數(shù)的調(diào)用,類的定義都會(huì)引入命名空間误窖,函數(shù)中的再定義函數(shù)叮盘,類中的成員函數(shù)定義會(huì)在局部namespace中再次引入局部namespace。

二霹俺、作用域

1柔吼、概述

A scope is a textual region of a Python program where a namespace is directly accessible. "Directly accessible" here means that an unqualified reference to a name attempts to find the name in the namespace.

作用域就是一個(gè) Python 程序可以直接訪問命名空間的正文區(qū)域。

  • Python 程序中丙唧,直接訪問一個(gè)變量愈魏,會(huì)從內(nèi)到外依次訪問所有的作用域直到找到,否則會(huì)報(bào)未定義的錯(cuò)誤想际。

  • Python 中培漏,程序的變量并不是在哪個(gè)位置都可以訪問的,訪問權(quán)限決定于這個(gè)變量是在哪里賦值的胡本。

  • Python 中牌柄, 變量的作用域決定了在哪一部分程序可以訪問哪個(gè)特定的變量名稱

2、作用域種類

作用域分為4類侧甫,分別如下:

  • L(Local):最內(nèi)層珊佣,包含局部變量蹋宦,比如一個(gè)函數(shù)/方法內(nèi)部。

  • E(Enclosing):包含了非局部(non-local)也非全局(non-global)的變量咒锻。比如兩個(gè)嵌套函數(shù)冷冗,一個(gè)函數(shù)(或類) A 里面又包含了一個(gè)函數(shù) B ,那么對(duì)于 B 中的名稱來說 A 中的作用域就為 nonlocal惑艇。

  • G(Global):當(dāng)前腳本的最外層蒿辙,比如當(dāng)前模塊的全局變量。

  • B(Built-in):包含了內(nèi)建的變量/關(guān)鍵字等滨巴,最后被搜索须板。

作用域規(guī)則順序?yàn)椋篖->E->G->B 如果變量在局部內(nèi)找不到,便會(huì)去局部外的局部找(例如閉包)兢卵,再找不到就會(huì)去全局找,再找不到就去內(nèi)置中找绪颖,如下圖所示:

image.gif

3秽荤、全局作用域和局部作用域

局部作用域 (Local)是腳本中的最內(nèi)層,包含局部變量柠横,比如一個(gè)函數(shù)或方法內(nèi)部窃款。閉包函數(shù)外函數(shù)(Enclosing)包含了非局部(non-local)也非全局(non-global)的變量。全局作用域(Global)是當(dāng)前腳本的最外層牍氛,如當(dāng)前模塊的全局變量晨继,實(shí)例如下:

以上實(shí)例展示的是全局作用域和閉包函數(shù)中的函數(shù),以及函數(shù)中的局部作用域搬俊,對(duì)于函數(shù) inner() 來說紊扬,outer() 中的作用域?yàn)?non-local

4、內(nèi)建作用域

Python 中的內(nèi)建作用域(Built-in):包含了內(nèi)建的變量/關(guān)鍵字等唉擂,最后被搜索

內(nèi)建作用域是通過一個(gè)名為 builtin 的標(biāo)準(zhǔn)模塊來實(shí)現(xiàn)的餐屎,但是這個(gè)變量名自身并沒有放入內(nèi)置作用域內(nèi),所以必須導(dǎo)入這個(gè)文件才能夠使用它玩祟。在Python3.0中腹缩,可以使用以下的代碼來查看到底預(yù)定義了哪些變量:

Python 中只有模塊(module),類(class)以及函數(shù)(def空扎、lambda)才會(huì)引入新的作用域藏鹊,其它的代碼塊(如 if/elif/else/、try/except转锈、for/while等)是不會(huì)引入新的作用域的盘寡,也就是說這些語句內(nèi)定義的變量,外部也可以訪問黑忱,如下:

實(shí)例中 result 變量定義在 if 語句塊中宴抚,但外部還是可以訪問的勒魔。

如果將 result 定義在函數(shù)中,則它就是局部變量菇曲,外部不能訪問冠绢,在代碼中會(huì)報(bào)錯(cuò)運(yùn)行出異常:

從以上報(bào)錯(cuò)信息看出,name2 未定義常潮,因?yàn)閚ame2 是函數(shù)names() 中的局部變量弟胀,只能在函數(shù)內(nèi)部調(diào)用,外部不能調(diào)用函數(shù)中的局部變量喊式。

5孵户、全局變量和局部變量

  • 全局變量:定義在函數(shù)外部擁有全局作用域的變量

  • 局部變量:定義在函數(shù)內(nèi)部擁有局部作用域的變量

局部變量只能在其被聲明的函數(shù)內(nèi)部訪問,而全局變量可以在整個(gè)程序范圍內(nèi)訪問岔留。調(diào)用函數(shù)時(shí)夏哭,所有在函數(shù)內(nèi)聲明的變量名稱都將被加入到作用域中。如下實(shí)例:

6献联、global 和 nonlocal關(guān)鍵字

當(dāng)內(nèi)部作用域想修改外部作用域的變量時(shí)竖配,就要用到global和nonlocal關(guān)鍵字了。

變量訪問順序
當(dāng)前作用域局部變量->外層作用域變量->再外層作用域變量->......->當(dāng)前模塊全局變量->pyhton內(nèi)置變量

  • global:全局變量,當(dāng)局部作用域改變?nèi)肿兞坑胓lobal里逆,同時(shí)global還可以定義新的全局變量

  • nonlocal:外層嵌套函數(shù)的變量,nonlocal不能定義新的外層函數(shù)變量进胯,只能改變已有的外層函數(shù)變量,同時(shí)nonlocal不能改變?nèi)肿兞?/p>

6.1 修改全局變量

以上實(shí)例輸出結(jié)果為:

6.2 修改嵌套作用域

如果要修改嵌套作用域(enclosing 作用域,外層非全局作用域)中的變量則需要 nonlocal 關(guān)鍵字

以上實(shí)例輸出:

另外還有一種特殊情況原押,以下這段代碼有語法錯(cuò)誤胁镐,運(yùn)行會(huì)報(bào)一個(gè)異常:

程序執(zhí)行異常:

錯(cuò)誤信息為局部作用域引用錯(cuò)誤,因?yàn)?test 函數(shù)中的 a 使用的是局部變量诸衔,未定義盯漂,無法修改。將 a 修改為全局變量笨农,通過函數(shù)參數(shù)傳遞宠能,程序就可以正常執(zhí)行,輸出結(jié)果為:

程序輸出結(jié)果為:

另一種解決辦法是加 global 關(guān)鍵字:

輸出結(jié)果為:

6.3 global 和 nonlocal 的區(qū)別

  • 兩者的功能不同磁餐。global關(guān)鍵字修飾變量后標(biāo)識(shí)該變量是全局變量违崇,對(duì)該變量進(jìn)行修改就是修改全局變量,而nonlocal關(guān)鍵字修飾變量后標(biāo)識(shí)該變量是上一級(jí)函數(shù)中的局部變量诊霹,如果上一級(jí)函數(shù)中不存在該局部變量羞延,nonlocal位置會(huì)發(fā)生錯(cuò)誤(最上層的函數(shù)使用nonlocal修飾變量必定會(huì)報(bào)錯(cuò))。

  • 兩者使用的范圍不同脾还。global關(guān)鍵字可以用在任何地方伴箩,包括最上層函數(shù)中和嵌套函數(shù)中,即使之前未定義該變量鄙漏,global修飾后也可以直接使用嗤谚,而nonlocal關(guān)鍵字只能用于嵌套函數(shù)中棺蛛,并且外層函數(shù)中定義了相應(yīng)的局部變量,否則會(huì)發(fā)生錯(cuò)誤

總結(jié)

本節(jié)給大家介紹了 Python 命名空間和作用用戶的介紹與簡單應(yīng)用巩步,在 Python 開發(fā)實(shí)戰(zhàn)中對(duì)命名空間和作用域的運(yùn)用比較廣泛旁赊,謹(jǐn)以此文獻(xiàn)給在 Python 學(xué)習(xí)道路上的道友,希望對(duì)大家有一絲幫助椅野。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末终畅,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子竟闪,更是在濱河造成了極大的恐慌离福,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件炼蛤,死亡現(xiàn)場離奇詭異妖爷,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)理朋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門赠涮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人暗挑,你說我怎么就攤上這事⌒庇眩” “怎么了炸裆?”我有些...
    開封第一講書人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長鲜屏。 經(jīng)常有香客問我烹看,道長,這世上最難降的妖魔是什么洛史? 我笑而不...
    開封第一講書人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任惯殊,我火速辦了婚禮,結(jié)果婚禮上也殖,老公的妹妹穿的比我還像新娘土思。我一直安慰自己,他們只是感情好忆嗜,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開白布己儒。 她就那樣靜靜地躺著,像睡著了一般捆毫。 火紅的嫁衣襯著肌膚如雪闪湾。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,541評(píng)論 1 305
  • 那天绩卤,我揣著相機(jī)與錄音途样,去河邊找鬼江醇。 笑死,一個(gè)胖子當(dāng)著我的面吹牛何暇,可吹牛的內(nèi)容都是我干的陶夜。 我是一名探鬼主播,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼赖晶,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼律适!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起遏插,我...
    開封第一講書人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤捂贿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后胳嘲,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體厂僧,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年了牛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了颜屠。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡鹰祸,死狀恐怖甫窟,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蛙婴,我是刑警寧澤粗井,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站街图,受9級(jí)特大地震影響浇衬,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜餐济,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一耘擂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧絮姆,春花似錦醉冤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至辕近,卻和暖如春韵吨,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來泰國打工归粉, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留椿疗,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓糠悼,卻偏偏與公主長得像届榄,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子倔喂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355