Python基礎(chǔ)手冊24——函數(shù)中變量的作用域

五、變量的作用域

當(dāng)你在一個(gè)程序中使用變量名時(shí)渐行,Python創(chuàng)建轰坊、改變或查找變量名都是在命名空間(一個(gè)保存變量名的地方,這個(gè)地方的范圍也叫作變量的作用域)中進(jìn)行的祟印。

在創(chuàng)建變量時(shí)肴沫,Python將變量名被創(chuàng)建的地點(diǎn)關(guān)聯(lián)給(綁定給)一個(gè)特定的命名空間。也就是說在代碼中變量創(chuàng)建的位置決定了這個(gè)變量將存在于哪個(gè)命名空間蕴忆,也就是它可以被訪問的范圍颤芬。

函數(shù)的作用域有助于防止程序之中變量名的沖突,并且有助于函數(shù)成為更加獨(dú)立的程序單元套鹅。


1站蝠、作用域

變量的作用域可以分為:本地作用域、全局作用域和內(nèi)置作用域卓鹿。

在任何情況下菱魔,一個(gè)變量名的作用域總是由變量在程序中被創(chuàng)建的位置所決定的,并且與函數(shù)被調(diào)用的地點(diǎn)完全沒有關(guān)系吟孙。

  1. 如果一個(gè)變量在函數(shù)內(nèi)創(chuàng)建澜倦,它被定位在這個(gè)函數(shù)之內(nèi),那么他的作用于就是本地的杰妓。
  2. 如果一個(gè)變量在一個(gè)嵌套的函數(shù)內(nèi)創(chuàng)建藻治,對于外層的函數(shù)來說,它是非本地的(也就是外層函數(shù)式無法訪問的)巷挥。
  3. 如果一個(gè)變量在函數(shù)之外(也就是在Python的頂級代碼快中)創(chuàng)建桩卵,它他的作用域就是全局的。


(1) 本地作用域

在一個(gè)函數(shù)內(nèi)部對一個(gè)變量名(不包含變量成員的引用,例如:name[1]等)任何類型的賦值(而不是在一個(gè)表達(dá)式中對其進(jìn)行引用)都會創(chuàng)建新的變量吸占,并把新創(chuàng)建的變量劃定為本地的作用域晴叨。這包括 = 語句、import 語句矾屯、def 語句兼蕊、函數(shù)參數(shù)名稱等。

在默認(rèn)的情況下件蚕,函數(shù)內(nèi)創(chuàng)建的所有變量都是與函數(shù)的本地命名空間相關(guān)聯(lián)的孙技。這意味著:一個(gè)在 def 內(nèi)定義的變量能夠被 def 內(nèi)的代碼使用,不能在函數(shù)的外部被引用排作。

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

本地變量作為臨時(shí)的變量名,只有在函數(shù)運(yùn)行時(shí)才需要他們衫生。


嵌套作用域

Python 為嵌套函數(shù)提供了嵌套的命名空間(作用域)裳瘪,使得嵌套在內(nèi)部的函數(shù)內(nèi)創(chuàng)建的變量名本地化,以便嵌套函數(shù)內(nèi)部使用的變量名不會與嵌套函數(shù)外的變量名產(chǎn)生沖突罪针。


閉合函數(shù)

嵌套作用域的查找在嵌套的函數(shù)已經(jīng)返回后也是有效的彭羹。這種行為有時(shí)也叫作閉合(closure)函數(shù) —— 一個(gè)能夠記住嵌套作用域的變量值的函數(shù),盡管那個(gè)作用域或許已經(jīng)不存在了泪酱。

這是一種相當(dāng)高級的技術(shù)派殷,除了那些擁有函數(shù)式編程背景的程序員們,以后在實(shí)際使用中并不常見墓阀。另一方面毡惜,嵌套的作用域常常被 lambda 函數(shù)創(chuàng)建表達(dá)式使用——因?yàn)樗麄兪潜磉_(dá)式,它們幾乎總是嵌套在一個(gè)函數(shù)中斯撮。此外虱黄,函數(shù)嵌套通常用作裝飾器 —— 在某些情況下,它是最合理的編碼模式吮成。

通常來說,類是一個(gè)更好的像這樣“記憶”的選擇辜梳,因?yàn)樗鼈冏尃顟B(tài)變得很明確粱甫。不使用類的話,全局變量作瞄、像這樣的嵌套作用域引用以及默認(rèn)的參數(shù)就是 Python 的函數(shù)能夠保留狀態(tài)信息的主要方法了茶宵。

在 Python 中作用域是可以做任意的嵌套的,但是我們應(yīng)當(dāng)盡量少的定義嵌套函數(shù)宗挥。


(2)全局作用域

函數(shù)定義了本地作用域乌庶,而模塊定義的是全局作用域种蝶。每個(gè)模塊都是一個(gè)全局作用域。

全局作用域的作用范圍僅限于單個(gè)文件瞒大。這里的全局指的是在一個(gè)文件的頂層創(chuàng)建的變量名僅對于這個(gè)文件內(nèi)部的代碼而言是全局的螃征。在 Python 中沒有基于一個(gè)單個(gè)文件的并可以應(yīng)用在任何其他文件的全局作用域。

全局變量的一個(gè)特征是除非被刪除掉透敌,否則它們將存活到文件運(yùn)行結(jié)束盯滚,且對于所有的函數(shù),他們的值都是可以被訪問的酗电。然而局部變量魄藕,就像它們存放的棧,只是在函數(shù)調(diào)用時(shí)暫時(shí)地存在撵术,僅僅只依賴于定義它們的函數(shù)現(xiàn)階段是否處于活動(dòng)狀態(tài)背率。


(3)內(nèi)置作用域

內(nèi)置作用域僅僅是一個(gè)名為 builtins 的內(nèi)置模塊,必須要 import builtins 之后才能使用這個(gè)模塊嫩与,因?yàn)樽兞棵?builtins 本身并沒有預(yù)先導(dǎo)入寝姿。

上面圖片中列表中的變量名組成了 Python 中的內(nèi)置作用域。前一半是內(nèi)置的異常蕴纳,后一半是內(nèi)置函數(shù)会油。由于下面要講的 LEGB 法則最后將自動(dòng)搜索這個(gè)模塊,將會自動(dòng)得到這個(gè)列表中的所有變量古毛。所以你能夠使用這些變量而不需要導(dǎo)入 builtins 模塊翻翩。


2、LEGB法則

Python的變量名解析機(jī)制稱為 LEGB 法則稻薇,這也是由作用域的命名而來的嫂冻。

當(dāng)在函數(shù)中使用變量時(shí),Python搜索4個(gè) 作用域:本地作用域(L)塞椎、之后是上一層結(jié)構(gòu)中的 def 或 lambda 的本地作用域(E)桨仿,之后是全局作用域(G),最后是內(nèi)置作用域(B)案狠。并且在第一處能夠找到這個(gè)變量名的地方停下來服傍。如果變量名在這次搜索中沒有找到,Python 會拋出 NameError 異常骂铁。


3吹零、global 語句

在默認(rèn)情況下,所有在一個(gè)函數(shù)中被賦值的變量都位于這個(gè)函數(shù)的本地作用域拉庵,并且僅在這個(gè)函數(shù)運(yùn)行的過程中存在灿椅。為了在函數(shù)內(nèi)創(chuàng)建或修改一個(gè)全局作用域的變量,需要使用 global 語句來聲明使用全局作用域。

global 語句是一個(gè)使用全局命名空間的聲明茫蛹,它告訴 Python 函數(shù)打算生成或修改一個(gè)或多個(gè)全局變量名操刀。global 使得作用域查找跳過本地作用域從全局作用域開始,如果變量不存在將繼續(xù)到內(nèi)置作用域婴洼。但是骨坑,對變量的賦值總是在全局作用域中創(chuàng)建或修改它們。

global 語句包含了關(guān)鍵字 global窃蹋,其后跟著一個(gè)或多個(gè)由逗號分開的變量名卡啰。當(dāng)函數(shù)主體調(diào)用時(shí),所有列出來的變量名將被映射到全局作用域內(nèi)警没。


最小化全局變量

在默認(rèn)情況下匈辱,函數(shù)內(nèi)部注冊的變量名是本地變量,這是有意而為之的杀迹。將其改為全局變量會引發(fā)一些軟件問題:由于變量的值取決于函數(shù)調(diào)用的順序亡脸,而函數(shù)自身是任意順序進(jìn)行排列的,導(dǎo)致了程序調(diào)試起來變得很困難树酪。

另一方面浅碾,不使用面向?qū)ο蟮木幊谭椒ㄒ约邦惖脑挘肿兞恳苍S就是Python中最直接保持全局狀態(tài)信息的方法(函數(shù)在下次被調(diào)用時(shí)需記住的信息):本地變量在函數(shù)返回時(shí)將會消失续语,而全局變量不是這樣垂谢。

在不熟悉編程的情況下,最好盡可能的避免使用全局變量疮茄。


最小化文件間的修改

盡管我們能夠直接修改另一個(gè)文件中的變量滥朱,但是往往我們都不這樣做。一個(gè)模塊文件的全局變量一旦被導(dǎo)入就成為了這個(gè)模塊對象的一個(gè)屬性:導(dǎo)入者自動(dòng)得到了這個(gè)被導(dǎo)入的模塊文件的所有全局變量的訪問權(quán)力试。

這會讓兩個(gè)文件有過強(qiáng)的相關(guān)性:假使它們都與變量X的值相關(guān)徙邻,如果沒有其中一個(gè)文件的話很難理解或重用另一個(gè)文件。這樣隱含的跨文件依賴性畸裳,在最好的情況下會導(dǎo)致代碼不靈活缰犁,最壞的情況會引發(fā) bug。

最好的解決辦法就是別這樣做:在文件間進(jìn)行通信最好的辦法就是通過調(diào)用函數(shù)怖糊,傳遞參數(shù)帅容,然后得到其返回值。


4伍伤、nonlocal 語句

如果需要在嵌套函數(shù)的內(nèi)層函數(shù)中直接使用外層函數(shù)中的變量丰嘉,可以使用 nonlocal 語句來做到。

nonlocal 應(yīng)用于嵌套內(nèi)層的函數(shù)作用域中的變量名嚷缭,而且在聲明 nonlocal 名稱的時(shí)候,他聲明的變量必須已經(jīng)存在于外層嵌套函數(shù)的作用域中 。

這就允許封閉的函數(shù)作為保留狀態(tài)的一個(gè)地方——當(dāng)一個(gè)函數(shù)調(diào)用的時(shí)候阅爽,信息被記住了——而不必使用共享的全局名稱路幸。

nonlocal 語句還加快了引用——就像 global 語句一樣,nonlocal 使得對該語句中列出的名稱的查找從嵌套的外層函數(shù)的作用域中開始付翁,而不是從所在函數(shù)的本地作用域開始简肴。也就是說,nonlocal 名稱只能出現(xiàn)在嵌套外層函數(shù)中百侧,作用域查找不會繼續(xù)到全局作用域或內(nèi)置作用域砰识。

當(dāng)執(zhí)行一條 nonlocal 語句時(shí),nonlocal 名稱必須已經(jīng)在一個(gè)嵌套的外層函數(shù)的作用域中創(chuàng)建佣渴,否則將會得到一個(gè)錯(cuò)誤——不能通過在嵌套的內(nèi)層函數(shù)的作用域中賦給它們一個(gè)新值來創(chuàng)建它們辫狼。


《Python基礎(chǔ)手冊》系列:

Python基礎(chǔ)手冊 1 —— Python語言介紹
Python基礎(chǔ)手冊 2 —— Python 環(huán)境搭建(Linux)
Python基礎(chǔ)手冊 3 —— Python解釋器
Python基礎(chǔ)手冊 4 —— 文本結(jié)構(gòu)
Python基礎(chǔ)手冊 5 —— 標(biāo)識符和關(guān)鍵字
Python基礎(chǔ)手冊 6 —— 操作符
Python基礎(chǔ)手冊 7 —— 內(nèi)建函數(shù)
Python基礎(chǔ)手冊 8 —— Python對象
Python基礎(chǔ)手冊 9 —— 數(shù)字類型
Python基礎(chǔ)手冊10 —— 序列(字符串)
Python基礎(chǔ)手冊11 —— 序列(元組&列表)
Python基礎(chǔ)手冊12 —— 序列(類型操作)
Python基礎(chǔ)手冊13 —— 映射(字典)
Python基礎(chǔ)手冊14 —— 集合
Python基礎(chǔ)手冊15 —— 解析
Python基礎(chǔ)手冊16 —— 文件
Python基礎(chǔ)手冊17 —— 簡單語句
Python基礎(chǔ)手冊18 —— 復(fù)合語句(流程控制語句)
Python基礎(chǔ)手冊19 —— 迭代器
Python基礎(chǔ)手冊20 —— 生成器
Python基礎(chǔ)手冊21 —— 函數(shù)的定義
Python基礎(chǔ)手冊22 —— 函數(shù)的參數(shù)
Python基礎(chǔ)手冊23 —— 函數(shù)的調(diào)用
Python基礎(chǔ)手冊24 —— 函數(shù)中變量的作用域
Python基礎(chǔ)手冊25 —— 裝飾器
Python基礎(chǔ)手冊26 —— 錯(cuò)誤 & 異常
Python基礎(chǔ)手冊27 —— 模塊
Python基礎(chǔ)手冊28 —— 模塊的高級概念
Python基礎(chǔ)手冊29 —— 包

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市辛润,隨后出現(xiàn)的幾起案子膨处,更是在濱河造成了極大的恐慌,老刑警劉巖砂竖,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件真椿,死亡現(xiàn)場離奇詭異,居然都是意外死亡乎澄,警方通過查閱死者的電腦和手機(jī)突硝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來置济,“玉大人解恰,你說我怎么就攤上這事≈廴猓” “怎么了修噪?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長路媚。 經(jīng)常有香客問我黄琼,道長,這世上最難降的妖魔是什么整慎? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任脏款,我火速辦了婚禮,結(jié)果婚禮上裤园,老公的妹妹穿的比我還像新娘撤师。我一直安慰自己,他們只是感情好拧揽,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布剃盾。 她就那樣靜靜地躺著腺占,像睡著了一般。 火紅的嫁衣襯著肌膚如雪痒谴。 梳的紋絲不亂的頭發(fā)上衰伯,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天,我揣著相機(jī)與錄音积蔚,去河邊找鬼意鲸。 笑死,一個(gè)胖子當(dāng)著我的面吹牛尽爆,可吹牛的內(nèi)容都是我干的怎顾。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼漱贱,長吁一口氣:“原來是場噩夢啊……” “哼槐雾!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起饱亿,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤蚜退,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后彪笼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體钻注,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年配猫,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了幅恋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,137評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡泵肄,死狀恐怖捆交,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情腐巢,我是刑警寧澤品追,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站冯丙,受9級特大地震影響肉瓦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜胃惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一泞莉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧船殉,春花似錦鲫趁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽堡僻。三九已至,卻和暖如春疫剃,著一層夾襖步出監(jiān)牢的瞬間苦始,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工慌申, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人理郑。 一個(gè)月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓蹄溉,卻偏偏與公主長得像,于是被迫代替她去往敵國和親您炉。 傳聞我的和親對象是個(gè)殘疾皇子柒爵,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 1、引言 最近在刷leetcode題的時(shí)候赚爵,遇到一個(gè)求最長回文子串的題目棉胀,于是,我寫了如下的代碼: 哎呀冀膝,寫了兩個(gè)...
    文哥的學(xué)習(xí)日記閱讀 14,294評論 6 32
  • Python作用域基礎(chǔ) 當(dāng)你在一個(gè)程序中適用變量名時(shí)窝剖,Python創(chuàng)建麻掸、改變或查找變量名都是在所謂的命名空間(一個(gè)...
    聽風(fēng)踏雪閱讀 351評論 0 0
  • Python作用域基礎(chǔ) 當(dāng)在程序中使用變量名時(shí),Python創(chuàng)建赐纱、改變或查找變量名都是在所謂的命名空間中進(jìn)行的脊奋。作...
    So_ProbuING閱讀 295評論 0 1
  • 函數(shù)和對象 1、函數(shù) 1.1 函數(shù)概述 函數(shù)對于任何一門語言來說都是核心的概念疙描。通過函數(shù)可以封裝任意多條語句诚隙,而且...
    道無虛閱讀 4,527評論 0 5
  • 不知道在每個(gè)人眼里,根是什么定義起胰,我的理解是生我養(yǎng)我的地方. 不知道從什么時(shí)候開始久又,我們每年回家的次數(shù)越來越少,也...
    DAJU閱讀 392評論 0 0