4. 執行模型?

4.1. 程序的結構?

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

代碼塊在 執行幀 中被執行。 一個(gè)幀會(huì )包含某些管理信息(用于調試)并決定代碼塊執行完成后應前往何處以及如何繼續執行。

4.2. 命名與綁定?

4.2.1. 名稱(chēng)的綁定?

名稱(chēng) 用于指代對象。 名稱(chēng)是通過(guò)名稱(chēng)綁定操作來(lái)引入的。

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 語(yǔ)句的目標也被視作一種綁定(雖然其實(shí)際語(yǔ)義為解除名稱(chēng)綁定)。

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

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

每個(gè)在程序文本中出現的名稱(chēng)是指由以下名稱(chēng)解析規則所建立的對該名稱(chēng)的 綁定。

4.2.2. 名稱(chēng)的解析?

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

當一個(gè)名稱(chēng)在代碼塊中被使用時(shí),會(huì )由包含它的最近作用域來(lái)解析。 對一個(gè)代碼塊可見(jiàn)的所有這種作用域的集合稱(chēng)為該代碼塊的 環(huán)境。

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

如果一個(gè)代碼塊內的任何位置發(fā)生名稱(chēng)綁定操作,則代碼塊內所有對該名稱(chēng)的使用會(huì )被認為是對當前代碼塊的引用。 當一個(gè)名稱(chēng)在其被綁定前就在代碼塊內被使用時(shí)則會(huì )導致錯誤。 這個(gè)一個(gè)很微妙的規則。 Python 缺少聲明語(yǔ)法,并允許名稱(chēng)綁定操作發(fā)生于代碼塊內的任何位置。 一個(gè)代碼塊的局部變量可通過(guò)在整個(gè)代碼塊文本中掃描名稱(chēng)綁定操作來(lá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 語(yǔ)句與同一代碼塊中名稱(chēng)綁定具有相同的作用域。 如果一個(gè)自由變量的最近包含作用域中有一條 global 語(yǔ)句,則該自由變量也會(huì )被當作是全局變量。

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

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

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

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

4.2.3. 內置命名空間和受限的執行?

CPython implementation detail: 用戶(hù)不應該接觸 __builtins__,嚴格說(shuō)來(lái)它屬于實(shí)現細節。 用戶(hù)如果要重載內置命名空間中的值則應該 import builtins 并相應地修改該模塊中的屬性。

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

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

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

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

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

4.3. 異常?

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

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

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

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

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 的組成部分。 其內容可能在 Python 升級到新版本時(shí)不經(jīng)警告地發(fā)生改變,不應該被需要在多版本解釋器中運行的代碼所依賴(lài)。

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

備注

1

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