Python 作用域和命名空間
在介紹類之前,我首先要告訴你一些Python的作用域規則。類定義對命名空間有一些巧妙的技巧,你需要知道作用域和命名空間如何工作才能完全理解正在發生的事情。順便說一下,關于這個主題的知識對任何高級Python程序員都很有用。

讓我們從一些定義開始。
namespace?是一個從名字到對象的映射。大部分命名空間當前都由?Python 字典實現,但一般情況下基本不會去關注它們(除了要面對性能問題時),而且也有可能在將來更改。下面是幾個命名空間的例子:存放Python內置函數的集合(包含?abs()?這樣的函數,和內建的異常等);模塊中的全局名稱;函數調用中的本地名稱。從某種意義上說,對象的的屬性集合也是一種命名空間的形式。關于Python命名空間的重要一點是,不同命名空間中的名稱之間絕對沒有關系;例如,兩個不同的模塊都可以定義函數“最大化”而不會產生混淆 - 模塊的用戶必須在其前面加上模塊名稱。
順便說明一下,我把任何跟在一個點號之后的名稱都稱為?屬性?--- 例如,在表達式?z.real?中,real?是對象?z?的一個屬性。按嚴格的說法,對模塊中名稱的引用屬于屬性引用:在表達式?modname.funcname?中,modname?是一個模塊對象而?funcname?是它的一個屬性。在此情況下在模塊的屬性和模塊中定義的全局名稱之間正好存在一個直觀的映射:它們共享相同的命名空間!
屬性可以是只讀或者可寫的。如果為后者,那么對屬性的賦值是可行的。模塊屬性是可以寫,你可以寫出?modname.the_answer?=?42?。可寫的屬性同樣可以用?del?語句刪除。例如,?del?modname.the_answer?將會從名為?modname?的對象中移除?the_answer?屬性。
在不同時刻創建的命名空間擁有不同的生存期。包含內置名稱的命名空間是在 Python 解釋器啟動時創建的,永遠不會被刪除。模塊的全局命名空間在模塊定義被讀入時創建;通常,模塊命名空間也會持續到解釋器退出。被解釋器的頂層調用執行的語句,從一個腳本文件讀取或交互式地讀取,被認為是?__main__?模塊調用的一部分,因此它們擁有自己的全局命名空間。(內置名稱實際上也存在于一個模塊中;這個模塊稱作?builtins?。)
python教程,一個函數的本地命名空間在這個函數被調用時創建,并在函數返回或拋出一個不在函數內部處理的錯誤時被刪除。(事實上,比起描述到底發生了什么,忘掉它更好。)當然,每次遞歸調用都會有它自己的本地命名空間。
一個?作用域?是一個命名空間可直接訪問的 Python 程序的文本區域。 這里的 “可直接訪問” 意味著對名稱的非限定引用會嘗試在命名空間中查找名稱。
Although scopes are determined statically, they are used dynamically. At any time during execution, there are at least three nested scopes whose namespaces are directly accessible:
最先搜索的最內部作用域包含局部名稱
從最近的封閉作用域開始搜索的任何封閉函數的范圍包含非局部名稱,也包括非全局名稱
倒數第二個作用域包含當前模塊的全局名稱
最外面的范圍(最后搜索)是包含內置名稱的命名空間
如果一個名稱被聲明為全局變量,則所有引用和賦值將直接指向包含該模塊的全局名稱的中間作用域。 要重新綁定在最內層作用域以外找到的變量,可以使用?nonlocal?語句聲明為非本地變量。 如果沒有被聲明為非本地變量,這些變量將是只讀的(嘗試寫入這樣的變量只會在最內層作用域中創建一個?新的?局部變量,而同名的外部變量保持不變)。
通常,當前局部作為域將(按字面文本)引用當前函數的局部名稱。 在函數以外,局部作用域將引用與全局作用域相一致的命名空間:模塊的命名空間。 類定義將在局部命名空間內再放置另一個命名空間。
重要的是應該意識到作用域是按字面文本來確定的:在一個模塊內定義的函數的全局作用域就是該模塊的命名空間,無論該函數從什么地方或以什么別名被調用。 另一方面,實際的名稱搜索是在運行時動態完成的 --- 但是,語言定義在?編譯時?是朝著靜態名稱解析的方向演化的,因此不要過于依賴動態名稱解析! (事實上,局部變量已經是被靜態確定了。)
Python 的一個特殊之處在于 -- 如果不存在生效的?global?語句 -- 對名稱的賦值總是進入最內層作用域。 賦值不會復制數據 --- 它們只是將名稱綁定到對象。 刪除也是如此:語句?del?x?會從局部命名空間的引用中移除對?x?的綁定。 事實上,所有引入新名稱的操作都使用局部作用域:特別地,import?語句和函數定義會在局部作用域中綁定模塊或函數名稱。
global?語句可被用來表明特定變量生存于全局作用域并且應當在其中被重新綁定;nonlocal?語句表明特定變量生存于外層作用域中并且應當在其中被重新綁定。
9.2.1. 作用域和命名空間示例
This?is?an?example?demonstrating?how?to?reference?the?different?scopes?and?namespaces,?and?how?global?and?nonlocal?affect?variable?binding:def?scope_test():????def?do_local(): ????????spam?=?"local?spam" ????def?do_nonlocal(): ????????nonlocal?spam ????????spam?=?"nonlocal?spam" ????def?do_global():????????global?spam ????????spam?=?"global?spam" ????spam?=?"test?spam" ????do_local()????print("After?local?assignment:",?spam) ????do_nonlocal()????print("After?nonlocal?assignment:",?spam) ????do_global()????print("After?global?assignment:",?spam) scope_test()print("In?global?scope:",?spam)
示例代碼的輸出是:
After?local?assignment:?test?spam After?nonlocal?assignment:?nonlocal?spam After?global?assignment:?nonlocal?spam In?global?scope:?global?spam
請注意?局部?賦值(這是默認狀態)不會改變?scope_test?對?spam?的綁定。?nonlocal?賦值會改變?scope_test?對?spam?的綁定,而?global?賦值會改變模塊層級的綁定。
您還可以在?global?賦值之前看到之前沒有?spam?的綁定。
Python
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。