4. 執(zhí)行模型?

4.1. 程序的結構?

Python 程序是由代碼塊構成的。 代碼塊 是被作為一個單元來執(zhí)行的一段 Python 程序文本。 以下幾個都屬于代碼塊:模塊、函數(shù)體和類定義。 交互式輸入的每條命令都是代碼塊。 一個腳本文件(作為標準輸入發(fā)送給解釋器或是作為命令行參數(shù)發(fā)送給解釋器的文件)也是代碼塊。 一條腳本命令(通過 -c 選項在解釋器命令行中指定的命令)也是代碼塊。 通過在命令行中使用 -m 參數(shù)作為最高層級腳本(即 __main__ 模塊)運行的模塊也是代碼塊。 傳遞給內(nèi)置函數(shù) eval()exec() 的字符串參數(shù)也是代碼塊。

代碼塊在 執(zhí)行幀 中被執(zhí)行。 一個幀會包含某些管理信息(用于調(diào)試)并決定代碼塊執(zhí)行完成后應前往何處以及如何繼續(xù)執(zhí)行。

4.2. 命名與綁定?

4.2.1. 名稱的綁定?

名稱 用于指代對象。 名稱是通過名稱綁定操作來引入的。

The following constructs bind names:

  • formal parameters to functions,

  • class definitions,

  • function definitions,

  • assignment expressions,

  • targets that are identifiers if occurring in an assignment:

    • for loop header,

    • after as in a with statement, except clause or in the as-pattern in structural pattern matching,

    • in a capture pattern in structural pattern matching

  • import statements.

The import statement of the form from ... import * binds all names defined in the imported module, except those beginning with an underscore. This form may only be used at the module level.

del 語句的目標也被視作一種綁定(雖然其實際語義為解除名稱綁定)。

每條賦值或導入語句均發(fā)生于類或函數(shù)內(nèi)部定義的代碼塊中,或是發(fā)生于模塊層級(即最高層級的代碼塊)。

如果名稱綁定在一個代碼塊中,則為該代碼塊的局部變量,除非聲明為 nonlocalglobal。 如果名稱綁定在模塊層級,則為全局變量。 (模塊代碼塊的變量既為局部變量又為全局變量。) 如果變量在一個代碼塊中被使用但不是在其中定義,則為 自由變量。

每個在程序文本中出現(xiàn)的名稱是指由以下名稱解析規(guī)則所建立的對該名稱的 綁定。

4.2.2. 名稱的解析?

作用域 定義了一個代碼塊中名稱的可見性。 如果代碼塊中定義了一個局部變量,則其作用域包含該代碼塊。 如果定義發(fā)生于函數(shù)代碼塊中,則其作用域會擴展到該函數(shù)所包含的任何代碼塊,除非有某個被包含代碼塊引入了對該名稱的不同綁定。

當一個名稱在代碼塊中被使用時,會由包含它的最近作用域來解析。 對一個代碼塊可見的所有這種作用域的集合稱為該代碼塊的 環(huán)境

當一個名稱完全找不到時,將會引發(fā) NameError 異常。 如果當前作用域為函數(shù)作用域,且該名稱指向一個局部變量,而此變量在該名稱被使用的時候尚未綁定到特定值,將會引發(fā) UnboundLocalError 異常。 UnboundLocalErrorNameError 的一個子類。

如果一個代碼塊內(nèi)的任何位置發(fā)生名稱綁定操作,則代碼塊內(nèi)所有對該名稱的使用會被認為是對當前代碼塊的引用。 當一個名稱在其被綁定前就在代碼塊內(nèi)被使用時則會導致錯誤。 這個一個很微妙的規(guī)則。 Python 缺少聲明語法,并允許名稱綁定操作發(fā)生于代碼塊內(nèi)的任何位置。 一個代碼塊的局部變量可通過在整個代碼塊文本中掃描名稱綁定操作來確定。

If the global statement occurs within a block, all uses of the names specified in the statement refer to the bindings of those names in the top-level namespace. Names are resolved in the top-level namespace by searching the global namespace, i.e. the namespace of the module containing the code block, and the builtins namespace, the namespace of the module builtins. The global namespace is searched first. If the names are not found there, the builtins namespace is searched. The global statement must precede all uses of the listed names.

global 語句與同一代碼塊中名稱綁定具有相同的作用域。 如果一個自由變量的最近包含作用域中有一條 global 語句,則該自由變量也會被當作是全局變量。

nonlocal 語句會使得相應的名稱指向之前在最近包含函數(shù)作用域中綁定的變量。 如果指定名稱不存在于任何包含函數(shù)作用域中則將在編譯時引發(fā) SyntaxError。

模塊的作用域會在模塊第一次被導入時自動創(chuàng)建。 一個腳本的主模塊總是被命名為 __main__。

類定義代碼塊以及傳給 exec()eval() 的參數(shù)是名稱解析上下文中的特殊情況。 類定義是可能使用并定義名稱的可執(zhí)行語句。 這些引用遵循正常的名稱解析規(guī)則,例外之處在于未綁定的局部變量將會在全局命名空間中查找。 類定義的命名空間會成為該類的屬性字典。 在類代碼塊中定義的名稱的作用域會被限制在類代碼塊中;它不會擴展到方法的代碼塊中 -- 這也包括推導式和生成器表達式,因為它們都是使用函數(shù)作用域實現(xiàn)的。 這意味著以下代碼將會失敗:

class A:
    a = 42
    b = list(a + i for i in range(10))

4.2.3. 內(nèi)置命名空間和受限的執(zhí)行?

CPython implementation detail: 用戶不應該接觸 __builtins__,嚴格說來它屬于實現(xiàn)細節(jié)。 用戶如果要重載內(nèi)置命名空間中的值則應該 import builtins 并相應地修改該模塊中的屬性。

與一個代碼塊的執(zhí)行相關聯(lián)的內(nèi)置命名空間實際上是通過在其全局命名空間中搜索名稱 __builtins__ 來找到的;這應該是一個字典或一個模塊(在后一種情況下會使用該模塊的字典)。 默認情況下,當在 __main__ 模塊中時,__builtins__ 就是內(nèi)置模塊 builtins;當在任何其他模塊中時,__builtins__ 則是 builtins 模塊自身的字典的一個別名。

4.2.4. 與動態(tài)特性的交互?

自由變量的名稱解析發(fā)生于運行時而不是編譯時。 這意味著以下代碼將打印出 42:

i = 10
def f():
    print(i)
i = 42
f()

eval()exec() 函數(shù)沒有對完整環(huán)境的訪問權限來解析名稱。 名稱可以在調(diào)用者的局部和全局命名空間中被解析。 自由變量的解析不是在最近包含命名空間中,而是在全局命名空間中。 1 exec()eval() 函數(shù)有可選參數(shù)用來重載全局和局部命名空間。 如果只指定一個命名空間,則它會同時作用于兩者。

4.3. 異常?

異常是中斷代碼塊的正??刂屏鞒桃员闾幚礤e誤或其他異常條件的一種方式。 異常會在錯誤被檢測到的位置 引發(fā),它可以被當前包圍代碼塊或是任何直接或間接發(fā)起調(diào)用發(fā)生錯誤的代碼塊的其他代碼塊所 處理

Python 解析器會在檢測到運行時錯誤(例如零作為被除數(shù))的時候引發(fā)異常。 Python 程序也可以通過 raise 語句顯式地引發(fā)異常。 異常處理是通過 try ... except 語句來指定的。 該語句的 finally 子句可被用來指定清理代碼,它并不處理異常,而是無論之前的代碼是否發(fā)生異常都會被執(zhí)行。

Python 的錯誤處理采用的是“終止”模型:異常處理器可以找出發(fā)生了什么問題,并在外層繼續(xù)執(zhí)行,但它不能修復錯誤的根源并重試失敗的操作(除非通過從頂層重新進入出錯的代碼片段)。

當一個異常完全未被處理時,解釋器會終止程序的執(zhí)行,或者返回交互模式的主循環(huán)。 無論是哪種情況,它都會打印?;厮菪畔?,除非是當異常為 SystemExit 的時候。

Exceptions are identified by class instances. The except clause is selected depending on the class of the instance: it must reference the class of the instance or a non-virtual base class thereof. The instance can be received by the handler and can carry additional information about the exceptional condition.

備注

異常消息不是 Python API 的組成部分。 其內(nèi)容可能在 Python 升級到新版本時不經(jīng)警告地發(fā)生改變,不應該被需要在多版本解釋器中運行的代碼所依賴。

另請參看 try 語句 小節(jié)中對 try 語句的描述以及 raise 語句 小節(jié)中對 raise 語句的描述。

備注

1

出現(xiàn)這樣的限制是由于通過這些操作執(zhí)行的代碼在模塊被編譯的時候并不可用。