命名空間表示標識符(identifier)的可見范圍,當前命名空間主要是通過Python字典實現的,不過通常不關心具體的實現方式(除非出于性能考慮),以后也有可能會改變其實現方式。有一些命名空間的例子:內置命名(像abs()這樣的函數,以及內置異常名)集,模塊中的全局命名,函數調用中的局部命名。某種意義上講對象的屬性集也是一個命名空間。有幾個點需要注意下:
第一:不同命名空間中的命名沒有任何聯系,例如兩個不同的模塊可能都會定義一個名為maximize的函數而不會發生混淆--用戶必須以模塊名為前綴來引用它們。
第二:不同的命名空間在不同的時刻創建,有不同的生存期。包含內置命名的命名空間在Python解釋器啟動時創建,會一直保留,不被刪除。模塊的全局命名空間在模塊定義被讀入時創建,通常,模塊命名空間也會一直保存到解釋器退出。由解釋器在最高層調用執行的語句,不管它是從腳本文件中讀入還是來自交互式輸入,都是__main__模塊的一部分,所以它們也擁有自己的命名空間。(內置命名也同樣被包含在一個模塊中,它被稱作__builtin__。)
第三:當調用函數時,就會為它創建一個局部命名空間,并且在函數返回或拋出一個并沒有在函數內部處理的異常時被刪除。(實際上,用遺忘來形容到底發生了什么更為貼切。)當然,每個遞歸調用都有自己的局部命名空間。
作用域是定義程序該如何搜索確切地“名字-對象”的名空間的層級關系。是一個Python程序可以直接訪問命名空間的正文區域。意思是一個對名稱的錯誤引用會嘗試在命名空間內查找。盡管作用域是靜態定義,在使用時他們都是動態的。每次執行時,至少有四個命名空間可以直接訪問的作用域嵌套在一起:
第一:innermostscope,包含局部命名的使用域在最里面,首先被搜索;
第二:enclosingscope,中層的作用域,是內層嵌套作用域搜索起點,包含非局部,但是也非全局的命名
第三:Globalscope,包含當前模塊的全局命名
第四:Built-inscope,包含內置命名的命名空間。
so,這么多的作用域,Python是按什么順序搜索對應作用域的呢?著名的”LEGB-rule”,即scope的搜索順序:Local->Enclosing->Global->Built-in;當有一個變量在local域中找不到時,Python會找上一層的作用域,即enclosing域(該域不一定存在)。enclosing域還找不到的時候,再往上一層,搜索模塊內的global域。最后,會在built-in域中搜索。對于最終沒有搜索到時,Python會拋出一個NameError異常。作用域可以嵌套。比如模塊導入時。這也是為什么不推薦使用froma_moduleimport*的原因,導入的變量可能被當前模塊覆蓋。下面是一個例子:
defouter():
a=0
b=1
definner():
printa
printb
b=4
inner()
if__name__=="__main__":outer()
Traceback(mostrecentcalllast):
File"E:/PycharmProjects/CodeStatisticsTools/test_namespaces.py",line37,in
outer()
File"E:/PycharmProjects/CodeStatisticsTools/test_namespaces.py",line18,inouter
inner()
File"E:/PycharmProjects/CodeStatisticsTools/test_namespaces.py",line15,ininner
printb
UnboundLocalError:localvariable'b'referencedbeforeassignment
如果去掉b=4就能正常運行,這是為啥呢?在沒有b=4這行代碼時,Python解釋器執行到inner()中的printb時,發現有個變量b在當前作用域(local)中無法找到變量b,于是嘗試從enclosingscope中查找,找到后就能正常打印了。加上這行代碼后,在當前作用域找到了b,但是變量b的賦值發生在print語句之后,于是拋出錯誤:變量在賦值前就被引用。賦值語句通常隱式地會創建一個局部(local)變量,即便該變量名已存在于賦值語句發生的上一層作用域中
以上內容為大家介紹了python命名空間和作用域,希望對大家有所幫助,如果想要了解更多Python相關知識,請關注IT培訓機構:千鋒教育。