Python中的作用域規(guī)則詳解
Python是靜態(tài)作用域語言,盡管它自身是一個動態(tài)語言。也就是說,在Python中變量的作用域是由它在源代碼中的位置決定的,這與C有些相似,但是Python與C在作用域方面的差異還是非常明顯的。
接下來會談?wù)揚(yáng)ython的作用域規(guī)則,在這中間也會說明一下Python與C在作用域方面的不同。
在Python 2.0及之前的版本中,Python只支持3種作用域,即局部作用域,全局作用域,內(nèi)置作用域;在Python 2.2中,Python正式引入了一種新的作用域 --- 嵌套作用域;在Python 2.1中,嵌套作用域可以作為一個選項(xiàng)被開啟;嵌套作用域的引入,本質(zhì)上為Python實(shí)現(xiàn)了對閉包的支持,關(guān)于閉包的知識,網(wǎng)上有很多解釋,這里就不詳細(xì)展開了。相應(yīng)地,變量查找順序由之前的LGB變成LEGB(L:Local,E:Enclosing,G:Global,B:Built-in)。
在Python中,并不是任何代碼塊都能引入新的作用域,這與C有很大的不同:
#include<stdio.h>
int main() {
if(2 > 0) {
int i = 0;
}
printf("i = %d", i);
return 0;
}
在這段代碼中,if子句引入了一個局部作用域,變量i就存在于這個局部作用域中,但對外不可見,因此,接下來在printf函數(shù)中對變量i的引用會引發(fā)編譯錯誤。
但是,在Python中卻并非如此:
if True:
i = 0
print i
在這段代碼中,if子句并沒有引入一個局部作用域,變量i仍然處在全局作用域中,因此,變量i對于接下來的print語句是可見的。
實(shí)際上,在Python中,只有模塊,類以及函數(shù)才會引入新的作用域,其它的代碼塊是不會引入新的作用域的。
在Python中,使用一個變量之前不必預(yù)先聲明它,但是在真正使用它之前,它必須已經(jīng)綁定到某個對象;而名字綁定將在當(dāng)前作用域中引入新的變量,同時屏蔽外層作用域中的同名變量,不論這個名字綁定發(fā)生在當(dāng)前作用域中的哪個位置。
def f():
print i
f()
運(yùn)行結(jié)果將顯示:NameError: global name 'i' is not defined。Python首先在函數(shù)f的本地作用域中查找變量i,查找失敗,接著在全局作用域和內(nèi)置作用域中查找變量i,仍然失敗,最終拋出NameError異常。
i = 0
def f():
i = 8
print i
f()
print i
運(yùn)行結(jié)果顯示:8和0。i = 8是一個名字綁定操作,它在函數(shù)f的局部作用域中引入了新的變量i,屏蔽了全局變量i,因此f內(nèi)部的print語句看到的是局部變量i,f外部的print語句看到的是全局變量i。
i = 0
def f():
print i
i = 0
f()
運(yùn)行結(jié)果顯示:UnboundLocalError: local variable 'i' referenced before assignment。在這個例子當(dāng)中,函數(shù)f中的變量i是局部變量,但是在print語句使用它的時候,它還未被綁定到任何對象之上,所以拋出異常。
print i
i = 0
不論是以交互的方式運(yùn)行,還是以腳本文件的方式運(yùn)行,結(jié)果都顯示:NameError: name 'i' is not defined。這里的輸出結(jié)果又與上一個例子不同,這是因?yàn)樗陧敿壸饔糜颍K作用域)的緣故。對于模塊代碼而言,代碼在執(zhí)行之前,沒有經(jīng)過什么預(yù)處理,但是對于函數(shù)體而言,代碼在運(yùn)行之前已經(jīng)經(jīng)過了一個預(yù)處理,因此不論名字綁定發(fā)生在作用域的那個位置,它都能感知出來。Python雖然是一個靜態(tài)作用域語言,但是名字查找確實(shí)動態(tài)發(fā)生的,因此直到運(yùn)行的時候,才會發(fā)現(xiàn)名字方面的問題。
在Python中,名字綁定在所屬作用域中引入新的變量,同時綁定到一個對象。名字綁定發(fā)生在以下幾種情況之下:
1.參數(shù)聲明:參數(shù)聲明在函數(shù)的局部作用域中引入新的變量;
2.賦值操作:對一個變量進(jìn)行初次賦值會在當(dāng)前作用域中引入新的變量,后續(xù)賦值操作則會重新綁定該變量;
3.類和函數(shù)定義:類和函數(shù)定義將類名和函數(shù)名作為變量引入當(dāng)前作用域,類體和函數(shù)體將形成另外一個作用域;
4.import語句:import語句在當(dāng)前作用域中引入新的變量,一般是在全局作用域;
5.for語句:for語句在當(dāng)前作用域中引入新的變量(循環(huán)變量);
6.except語句:except語句在當(dāng)前作用域中引入新的變量(異常對象)。
在Python中,類定義所引入的作用域?qū)τ诔蓡T函數(shù)是不可見的,這與C++或者Java是很不同的,因此在Python中,成員函數(shù)想要引用類體定義的變量,必須通過self或者類名來引用它。
嵌套作用域的加入,會導(dǎo)致一些代碼編譯不過或者得到不同的運(yùn)行結(jié)果,在這里Python解釋器會幫助你識別這些可能引起問題的地方,給出警告。
locals函數(shù)返回所有的局部變量,但是不會返回嵌套作用域中的變量,實(shí)際上沒有函數(shù)會返回嵌套作用域中的變量。
相關(guān)文章
Python的條件語句與運(yùn)算符優(yōu)先級詳解
這篇文章主要介紹了Python的條件語句與運(yùn)算符優(yōu)先級,是Python入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下2015-10-10Python監(jiān)控服務(wù)器實(shí)用工具psutil使用解析
這篇文章主要介紹了Python監(jiān)控服務(wù)器實(shí)用工具psutil使用解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-12-12分析語音數(shù)據(jù)增強(qiáng)及python實(shí)現(xiàn)
數(shù)據(jù)增強(qiáng)是一種生成合成數(shù)據(jù)的方法,即通過調(diào)整原始樣本來創(chuàng)建新樣本。這樣我們就可獲得大量的數(shù)據(jù)。這不僅增加了數(shù)據(jù)集的大小,還提供了單個樣本的多個變體,這有助于我們的機(jī)器學(xué)習(xí)模型避免過度擬合2021-06-06Jupyter Notebook讀取csv文件出現(xiàn)的問題及解決
這篇文章主要介紹了Jupyter Notebook讀取csv文件出現(xiàn)的問題及解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-01-01Python基于多線程實(shí)現(xiàn)ping掃描功能示例
這篇文章主要介紹了Python基于多線程實(shí)現(xiàn)ping掃描功能,結(jié)合實(shí)例形式分析了Python多線程與進(jìn)程相關(guān)模塊調(diào)用操作技巧,需要的朋友可以參考下2018-07-07Python實(shí)現(xiàn)手機(jī)號自動判斷男女性別(實(shí)例解析)
這篇文章主要介紹了Python實(shí)現(xiàn)手機(jī)號自動判斷男女性別,本文性別判斷主要依靠airtest中的自動化測試實(shí)現(xiàn),通過實(shí)例代碼給大家講解的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2019-12-12用Python進(jìn)行TCP網(wǎng)絡(luò)編程的教程
這篇文章主要介紹了用Python進(jìn)行TCP網(wǎng)絡(luò)編程的教程,是Python學(xué)習(xí)當(dāng)中的基礎(chǔ)知識,代碼基于Python2.x版本,需要的朋友可以參考下2015-04-04python實(shí)現(xiàn)炫酷屏幕保護(hù)的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何利用python實(shí)現(xiàn)炫酷屏幕保護(hù)效果,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價值,感興趣的小伙伴可以跟隨小編一起了解一下2023-12-12