### 4. 執行模型
### 4.1. 命名和綁定
*名稱*是對象的引用。名稱通過名稱綁定操作引入。程序文本中名稱的每一次出現都會引用名稱的*綁定*,這種綁定在包含名稱使用的最內層函數塊中建立。
*塊*是Python 程序文本的一個片段,作為一個單元執行。下面這些都是塊:模塊、函數體、類定義。交互式敲入的每一個命令都是塊。一個腳本文件(作為解釋器標準輸入的文件或者在解釋器的命令行中指定的第一個參數)是一個代碼塊。一個腳本命令(在解釋器的命令行中以‘**-c**’選項指定的命令)是一個代碼塊。由內建函數[execfile()](# "execfile")讀入的文件是一個代碼塊。傳遞給[eval()](# "eval")內建函數和[exec](#)語句的字符串參數是一個代碼塊。由內建函數[input()](# "input")讀入并執行的表達式是一個代碼塊。
代碼塊在*執行幀*中執行。幀包含一些管理信息(用于調試)并決定代碼塊執行完成之后從哪里以及如何繼續執行。
*定義域*定義塊中名稱的可見性。如果局部變量定義在一個塊中,它的定義域就包含這個塊。如果定義出現在函數塊中,定義域將擴展到該定義塊中所包含的任何塊,除非被包含的塊為該名稱引入一個不同的綁定。類代碼塊中定義的名稱的定義域限制在類代碼塊中;它不會擴展到方法的代碼塊中 - 包括生成器表達式,因為它們使用函數的定義域實現。這意味著下面的寫法將會失敗:
~~~
class A:
a = 42
b = list(a + i for i in range(10))
~~~
當名稱在代碼塊中使用時,使用包含它的最內層定義域解析。對于一個代碼塊可見的定義域集合稱為該代碼塊的*環境*。
如果名稱綁定在代碼塊中,那么它是該代碼塊的局部變量。如果名稱綁定在模塊的級別,那么它是一個全局變量。(模塊代碼塊的變量既是局部的又是全局的。)如果一個變量在代碼塊中使用但是沒有在那里定義,那么它是*自由變量*。
當完全找不到名稱時,引發一個[NameError](# "exceptions.NameError")異常。如果名稱引用一個沒有綁定的局部變量,引發一個[UnboundLocalError](# "exceptions.UnboundLocalError")異常。[UnboundLocalError](# "exceptions.UnboundLocalError")是[NameError](# "exceptions.NameError")的子類。
下面的句法結構將會綁定名稱:函數的形式參數、[import](#)語句、類和函數定義(綁定類或函數的名稱于定義它們的代碼塊中)、出現在賦值語句中的目標是標識符、[for](#)循環的頭部、[except](#)子句頭部的第二個位置或者[with](#)語句中[as](#)的后面。from...import*形式的[import](#)語句綁定導入的模塊中定義的所有名稱,除了以下劃線開頭的那些。這種形式只可以在模塊級別使用。
出現在[del](#)語句中的目標也被認為是這種目的的綁定(盡管真實的語義是取消名稱的綁定)。解綁定一個被封閉的定義域引用的名稱是非法的;編譯器將會報一個[SyntaxError](# "exceptions.SyntaxError")。
每個賦值和導入語句出現在類或函數定義的代碼塊中或者出現在模塊級別(頂級代碼塊)。
如果在代碼塊的任意地方出現名稱綁定操作,那么代碼塊中該名稱的所有使用將被當做對當前代碼塊的引用。當名稱在一個代碼塊中綁定之前使用時將導致錯誤。這個規則是微妙的。Python缺少聲明并允許名稱綁定操作出現在代碼塊內任何地方。代碼塊的局部變量通過掃描代碼塊的全部文本的名稱綁定操作決定。
如果代碼塊中出現global語句,那么所有使用該語句指明的名稱都是引用頂級命名空間中的名稱綁定。頂級命名空間中名稱通過查找全局命名空間解析,即模塊的命名空間包括代碼塊、內建的命名空間以及[__builtin__](# "__builtin__: The module that provides the built-in namespace.")模塊的命名空間。首先查找全局命名空間。如果那里找不到名稱,就會查找內建的命名空間。global語句必須在該名稱的所有使用之前。
與執行的代碼塊相關聯的內建命名空間實際上是通過查找它的全局命名空間中的__builtins__名稱找到的;它應該是一個字典或者一個模塊(如果是后一種情況,將使用模塊的字典)。默認情況下,在[__main__](# "__main__: The environment where the top-level script is run.")模塊中時,__builtins__就是內建的模塊[__builtin__](# "__builtin__: The module that provides the built-in namespace.")(注意:沒有‘s’);在其它任何模塊的時候,__builtins__是[__builtin__](# "__builtin__: The module that provides the built-in namespace.")模塊自身的字典的別名。__builtins__可以被設置成一個用戶創建的字典以創建一個嚴格執行的弱形式。
**CPython實現細節:**使用者不應該觸動__builtins__;嚴格地講它是實現細節。使用者如果想要覆蓋內建命名空間中的變量應該[導入](#)該[__builtin__](# "__builtin__: The module that provides the built-in namespace.")(沒有‘s’)模塊并適當地修改它的屬性。
模塊的命名空間在模塊第一次導入時自動創建。腳本的主模塊總是被稱為[__main__](# "__main__: The environment where the top-level script is run.")。
[global](#)語句具有和同一個代碼塊中名稱綁定操作相同的定義域。如果包含自由變量的最內層定義域包含一條global語句,那么這個自由變量被認為是一個全局變量。
類定義是一條可以使用和定義名稱的可執行語句。這些引用遵循名稱解析的正常規則。類定義的命名空間變成類的屬性字典。類定義域中定義的名稱在方法中不可見。
#### 4.1.1. 與動態功能的交互
當與包含自由變量的嵌套定義域聯合使用的時候,有幾種Python語句是非法的情況。
如果變量在一個包含它的定義域中被引用,那么刪除它的名稱是非法的。在編譯的時刻將會報告一個錯誤。
如果在函數中使用通配符形式的導入— import* —并且函數包含或者是一個帶有自由變量的嵌套代碼塊,那么編譯器將會引發一個[SyntaxError](# "exceptions.SyntaxError")。
如果函數中使用[exec](#)且函數包含或者是一個帶有自由變量的嵌套代碼塊,那么編譯器將會引發一個[SyntaxError](# "exceptions.SyntaxError")除非執行為[exec](#)顯式指明局部命名空間。(換句話說,execobj是非法的,而execobjinns是合法的。)
[eval()](# "eval")、[execfile()](# "execfile")和[input()](# "input")函數以及[exec](#)語句沒有訪問完整環境的權限來解析名稱。名稱可以在調用者的局部和全局命名空間中解析。自由變量不是在包含它們的最內層命名空間中解析,而是在全局命名空間中。[[1]](#)[exec](#)語句以及[eval()](# "eval")和[execfile()](# "execfile")函數具有可選參數以覆蓋全局和局部命名空間。如果只指明一個命名空間,則兩個命名空間都會使用它。
### 4.2. 異常
異常是一種打斷代碼塊的正常控制流程以處理錯誤或者其它意外情況的方法。異常在錯誤檢測到的點*引發*;它可以通過包圍它的代碼塊或者直接或間接調用發生錯誤的代碼塊的代碼塊*處理*。
Python解釋器在檢測到運行時錯誤(例如除0)時會引發一個異常。Python還可以通過[raise](#)語句顯示地引發異常。異常處理器通過[try](#) ... [except](#)語句指定。這種語句的[finally](#)子句可以用來指定清除代碼,它不處理異常,而是在前面的代碼中無論有沒有出現異常都會執行。?
Python 異常處理使用“終止”模型:異常處理器可以查明發生了什么并在外層繼續執行,但是它不可以修復錯誤的根源并重試失敗的操作。(除非通過從頂層重新進入出錯的代碼片段)。
當一個異常沒有被任何處理,那么解釋器會終止程序的執行或者返回到其交互式的主循環。在任何一種情況下,它都會打印出一個棧回溯,除了異常是[SystemExit](# "exceptions.SystemExit")的時候。
異常通過類的實例標識。[except](#)子句的選擇依賴于類的實例:它必須引用實例的類或者其基類。實例可以通過處理器接收并且可以帶有異常條件的額外信息。
異常也可以通過字符串標識,在這種情況下[except](#)子句通過對象的ID 選擇。任意一個值可能會跟隨標識字符串一起引發并傳遞給處理器。
注意
異常的消息不是Python API的一部分。它們的內容可能隨著Python 版本不斷地改變而沒有警告,在多種不同版本的解釋器下運行的代碼不應該依賴這些內容。
另請參考[*try語句*](#)小節中的[try](#)語句和*[raise語句](#)*小節中的[raise](#)語句。
腳注
| [[1]](#) | 出現這種限制是因為通過這些操作執行的代碼在模塊編譯的時候不可以訪問。 |
|-----|-----|
- Python 2 教程
- 1. 吊吊你的胃口
- 2. Python 解釋器
- 3. Python簡介
- 4. 控制流
- 5. 數據結構
- 6. 模塊
- 7. 輸入和輸出
- 8. 錯誤和異常
- 9. 類
- 10. 標準庫概覽
- 11. 標準庫概覽 — 第II部分
- 12.現在怎么辦?
- 13. 交互式輸入的編輯和歷史記錄
- 14. 浮點數運算:問題和局限
- Python 2 標準庫
- 1. 引言
- 2. 內建函數
- 3. 不太重要的內建函數
- 4. 內建的常量
- 5. 內建的類型
- 6. 內建的異常
- 7. String Services
- 8. Data Types
- 9. Numeric and Mathematical Modules
- 10. File and Directory Access
- 11. Data Persistence
- 13. File Formats
- 14. Cryptographic Services
- 15. Generic Operating System Services
- 16. Optional Operating System Services
- 17. Interprocess Communication and Networking
- 18. Internet Data Handling
- 20. Internet Protocols and Support
- 26. Debugging and Profiling
- 28. Python Runtime Services
- Python 2 語言參考
- 1. 簡介
- 2. 詞法分析
- 3. 數據模型
- 4. 執行模型
- 5. 表達式
- 6. 簡單語句
- 7. 復合語句
- 8. 頂層的組件
- 9. 完整的語法規范
- Python 3 教程
- 1. 引言
- 2. Python 解釋器
- 3. Python簡介
- 4. 控制流
- 5. 數據結構
- 6. 模塊
- 7. 輸入和輸出
- 8. 錯誤和異常
- 9. 類
- 10. 標準庫概覽
- 11. 標準庫概覽 — 第II部分
- 12.現在怎么辦?
- 13. 交互式輸入的編輯和歷史記錄
- 14. 浮點數運算:問題和局限