在介紹類之前,我首先介紹一些有關 Python 作用域的規則。類的定義非常巧妙的運用了命名空間,要完全理解接下來的知識,需要先理解作用域和命名空間的工作原理。另外,這一切的知識對于任何高級 Python 程序員都非常有用。
讓我們從一些定義說起。
_命名空間_?是從命名到對象的映射。當前命名空間主要是通過 Python 字典實現的,不過通常不關心具體的實現方式(除非出于性能考慮),以后也有可能會改變其實現方式。以下有一些命名空間的例子:內置命名(像?abs()?這樣的函數,以及內置異常名)集,模塊中的全局命名,函數調用中的局部命名。某種意義上講對象的屬性集也是一個命名空間。關于命名空間需要了解的一件很重要的事就是不同命名空間中的命名沒有任何聯系,例如兩個不同的模塊可能都會定義一個名為?maximize的函數而不會發生混淆-用戶必須以模塊名為前綴來引用它們。
順便提一句,我稱 Python 中任何一個“.”之后的命名為?_屬性_?--例如,表達式?z.real?中的?real?是對象?z?的一個屬性。嚴格來講,從模塊中引用命名是引用屬性:表達式?modname.funcname中,modname?是一個模塊對象,funcname?是它的一個屬性。因此,模塊的屬性和模塊中的全局命名有直接的映射關系:它們共享同一命名空間![[1]](http://www.pythondoc.com/pythontutorial3/classes.html#id19)
屬性可以是只讀過或寫的。后一種情況下,可以對屬性賦值。你可以這樣作:modname.the_answer?=?42?。可寫的屬性也可以用?del?語句刪除。例如:?del?modname.the_answer?會從?modname?對象中刪除?the_answer?屬性。
不同的命名空間在不同的時刻創建,有不同的生存期。包含內置命名的命名空間在 Python 解釋器啟動時創建,會一直保留,不被刪除。模塊的全局命名空間在模塊定義被讀入時創建,通常,模塊命名空間也會一直保存到解釋器退出。由解釋器在最高層調用執行的語句,不管它是從腳本文件中讀入還是來自交互式輸入,都是?__main__?模塊的一部分,所以它們也擁有自己的命名空間(內置命名也同樣被包含在一個模塊中,它被稱作?__builtin__?)。
當調用函數時,就會為它創建一個局部命名空間,并且在函數返回或拋出一個并沒有在函數內部處理的異常時被刪除。(實際上,用遺忘來形容到底發生了什么更為貼切。)當然,每個遞歸調用都有自己的局部命名空間。
_作用域_?就是一個 Python 程序可以直接訪問命名空間的正文區域。這里的直接訪問意思是一個對名稱的錯誤引用會嘗試在命名空間內查找。
盡管作用域是靜態定義,在使用時他們都是動態的。每次執行時,至少有三個命名空間可以直接訪問的作用域嵌套在一起:
* 包含局部命名的使用域在最里面,首先被搜索;其次搜索的是中層的作用域,這里包含了同級的函數;最后搜索最外面的作用域,它包含內置命名。
* 首先搜索最內層的作用域,它包含局部命名任意函數包含的作用域,是內層嵌套作用域搜索起點,包含非局部,但是也非全局的命名
* 接下來的作用域包含當前模塊的全局命名
* 最外層的作用域(最后搜索)是包含內置命名的命名空間
如果一個命名聲明為全局的,那么所有的賦值和引用都直接針對包含模全局命名的中級作用域。另外,從外部訪問到的所有內層作用域的變量都是只讀的。(試圖寫這樣的變量只會在內部作用域創建一個?_新_?局部變量,外部標示命名的那個變量不會改變)。
通常,局部作用域引用當前函數的命名。在函數之外,局部作用域與全局使用域引用同一命名空間:模塊命名空間。類定義也是局部作用域中的另一個命名空間。
重要的是作用域決定于源程序的意義:一個定義于某模塊中的函數的全局作用域是該模塊的命名空間,而不是該函數的別名被定義或調用的位置,了解這一點非常重要。另一方面,命名的實際搜索過程是動態的,在運行時確定的——然而,Python 語言也在不斷發展,以后有可能會成為靜態的“編譯”時確定,所以不要依賴動態解析!(事實上,局部變量已經是靜態確定了。)
Python 的一個特別之處在于:如果沒有使用?global?語法,其賦值操作總是在最里層的作用域。賦值不會復制數據,只是將命名綁定到對象。刪除也是如此:del?x?只是從局部作用域的命名空間中刪除命名?x?。事實上,所有引入新命名的操作都作用于局部作用域。特別是?import?語句和函數定將模塊名或函數綁定于局部作用域(可以使用?global?語句將變量引入到全局作用域)。
global?語句用以指明某個特定的變量為全局作用域,并重新綁定它。nonlocal?語句用以指明某個特定的變量為封閉作用域,并重新綁定它。
### 9.2.1\. 作用域和命名空間示例
以下是一個示例,演示了如何引用不同作用域和命名空間,以及?global?和?nonlocal?如何影響變量綁定:
~~~
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
~~~
注意:_local_?賦值語句是無法改變?_scope_test_?的?_spam_?綁定。nonlocal?賦值語句改變了?_scope_test_?的_spam_?綁定,并且?global?賦值語句從模塊級改變了 spam 綁定。
你也可以看到在?global?賦值語句之前對 spam 是沒有預先綁定的。
- Python 入門指南
- 1. 開胃菜
- 2. 使用 Python 解釋器
- 2.1. 調用 Python 解釋器
- 2.2. 解釋器及其環境
- 3. Python 簡介
- 3.1. 將 Python 當做計算器
- 3.2. 編程的第一步
- 4. 深入 Python 流程控制
- 4.1. if 語句
- 4.2. for 語句
- 4.3. range() 函數
- 4.4. break 和 continue 語句, 以及循環中的 else 子句
- 4.5. pass 語句
- 4.6. 定義函數
- 4.7. 深入 Python 函數定義
- 4.8. 插曲:編碼風格
- 5. 數據結構
- 5.1. 關于列表更多的內容
- 5.2. del 語句
- 5.3. 元組和序列
- 5.4. 集合
- 5.5. 字典
- 5.6. 循環技巧
- 5.7. 深入條件控制
- 5.8. 比較序列和其它類型
- 6. 模塊
- 6.1. 深入模塊
- 6.2. 標準模塊
- 6.3. dir() 函數
- 6.4. 包
- 7. 輸入和輸出
- 7.1. 格式化輸出
- 7.2. 文件讀寫
- 8. 錯誤和異常
- 8.1. 語法錯誤
- 8.2. 異常
- 8.3. 異常處理
- 8.4. 拋出異常
- 8.5. 用戶自定義異常
- 8.6. 定義清理行為
- 8.7. 預定義清理行為
- 9. 類
- 9.1. 術語相關
- 9.2. Python 作用域和命名空間
- 9.3. 初識類
- 9.4. 一些說明
- 9.5. 繼承
- 9.6. 私有變量
- 9.7. 補充
- 9.8. 異常也是類
- 9.9. 迭代器
- 9.10. 生成器
- 9.11. 生成器表達式
- 10. Python 標準庫概覽
- 10.1. 操作系統接口
- 10.2. 文件通配符
- 10.3. 命令行參數
- 10.4. 錯誤輸出重定向和程序終止
- 10.5. 字符串正則匹配
- 10.6. 數學
- 10.7. 互聯網訪問
- 10.8. 日期和時間
- 10.9. 數據壓縮
- 10.10. 性能度量
- 10.11. 質量控制
- 10.12. “瑞士軍刀”
- 11. 標準庫瀏覽 – Part II
- 11.1. 輸出格式
- 11.2. 模板
- 11.3. 使用二進制數據記錄布局
- 11.4. 多線程
- 11.5. 日志
- 11.6. 弱引用
- 11.7. 列表工具
- 11.8. 十進制浮點數算法
- 12. 接下來?
- 13. 交互式輸入行編輯歷史回溯
- 13.1. 行編輯
- 13.2. 歷史回溯
- 13.3. 快捷鍵綁定
- 13.4. 其它交互式解釋器
- 14. 浮點數算法:爭議和限制
- 14.1. 表達錯誤
- 15. 附錄
- 15.1. 交互模式