4. 執(zhí)行模型?
4.1. 程序的結(jié)構(gòu)?
Python 程序是由代碼塊構(gòu)成的。 代碼塊 是被作為一個(gè)單元來執(zhí)行的一段 Python 程序文本。 以下幾個(gè)都屬于代碼塊:模塊、函數(shù)體和類定義。 交互式輸入的每條命令都是代碼塊。 一個(gè)腳本文件(作為標(biāo)準(zhǔn)輸入發(fā)送給解釋器或是作為命令行參數(shù)發(fā)送給解釋器的文件)也是代碼塊。 一條腳本命令(通過 -c
選項(xiàng)在解釋器命令行中指定的命令)也是代碼塊。 通過在命令行中使用 -m
參數(shù)作為最高層級腳本(即 __main__
模塊)運(yùn)行的模塊也是代碼塊。 傳遞給內(nèi)置函數(shù) eval()
和 exec()
的字符串參數(shù)也是代碼塊。
代碼塊在 執(zhí)行幀 中被執(zhí)行。 一個(gè)幀會包含某些管理信息(用于調(diào)試)并決定代碼塊執(zhí)行完成后應(yīng)前往何處以及如何繼續(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:
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
語句的目標(biāo)也被視作一種綁定(雖然其實(shí)際語義為解除名稱綁定)。
每條賦值或?qū)胝Z句均發(fā)生于類或函數(shù)內(nèi)部定義的代碼塊中,或是發(fā)生于模塊層級(即最高層級的代碼塊)。
如果名稱綁定在一個(gè)代碼塊中,則為該代碼塊的局部變量,除非聲明為 nonlocal
或 global
。 如果名稱綁定在模塊層級,則為全局變量。 (模塊代碼塊的變量既為局部變量又為全局變量。) 如果變量在一個(gè)代碼塊中被使用但不是在其中定義,則為 自由變量。
每個(gè)在程序文本中出現(xiàn)的名稱是指由以下名稱解析規(guī)則所建立的對該名稱的 綁定。
4.2.2. 名稱的解析?
作用域 定義了一個(gè)代碼塊中名稱的可見性。 如果代碼塊中定義了一個(gè)局部變量,則其作用域包含該代碼塊。 如果定義發(fā)生于函數(shù)代碼塊中,則其作用域會擴(kuò)展到該函數(shù)所包含的任何代碼塊,除非有某個(gè)被包含代碼塊引入了對該名稱的不同綁定。
當(dāng)一個(gè)名稱在代碼塊中被使用時(shí),會由包含它的最近作用域來解析。 對一個(gè)代碼塊可見的所有這種作用域的集合稱為該代碼塊的 環(huán)境。
當(dāng)一個(gè)名稱完全找不到時(shí),將會引發(fā) NameError
異常。 如果當(dāng)前作用域?yàn)楹瘮?shù)作用域,且該名稱指向一個(gè)局部變量,而此變量在該名稱被使用的時(shí)候尚未綁定到特定值,將會引發(fā) UnboundLocalError
異常。 UnboundLocalError
為 NameError
的一個(gè)子類。
如果一個(gè)代碼塊內(nèi)的任何位置發(fā)生名稱綁定操作,則代碼塊內(nèi)所有對該名稱的使用會被認(rèn)為是對當(dāng)前代碼塊的引用。 當(dāng)一個(gè)名稱在其被綁定前就在代碼塊內(nèi)被使用時(shí)則會導(dǎo)致錯(cuò)誤。 這個(gè)一個(gè)很微妙的規(guī)則。 Python 缺少聲明語法,并允許名稱綁定操作發(fā)生于代碼塊內(nèi)的任何位置。 一個(gè)代碼塊的局部變量可通過在整個(gè)代碼塊文本中掃描名稱綁定操作來確定。
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
語句與同一代碼塊中名稱綁定具有相同的作用域。 如果一個(gè)自由變量的最近包含作用域中有一條 global 語句,則該自由變量也會被當(dāng)作是全局變量。
nonlocal
語句會使得相應(yīng)的名稱指向之前在最近包含函數(shù)作用域中綁定的變量。 如果指定名稱不存在于任何包含函數(shù)作用域中則將在編譯時(shí)引發(fā) SyntaxError
。
模塊的作用域會在模塊第一次被導(dǎo)入時(shí)自動創(chuàng)建。 一個(gè)腳本的主模塊總是被命名為 __main__
。
類定義代碼塊以及傳給 exec()
和 eval()
的參數(shù)是名稱解析上下文中的特殊情況。 類定義是可能使用并定義名稱的可執(zhí)行語句。 這些引用遵循正常的名稱解析規(guī)則,例外之處在于未綁定的局部變量將會在全局命名空間中查找。 類定義的命名空間會成為該類的屬性字典。 在類代碼塊中定義的名稱的作用域會被限制在類代碼塊中;它不會擴(kuò)展到方法的代碼塊中 -- 這也包括推導(dǎo)式和生成器表達(dá)式,因?yàn)樗鼈兌际鞘褂煤瘮?shù)作用域?qū)崿F(xiàn)的。 這意味著以下代碼將會失敗:
class A:
a = 42
b = list(a + i for i in range(10))
4.2.3. 內(nèi)置命名空間和受限的執(zhí)行?
CPython implementation detail: 用戶不應(yīng)該接觸 __builtins__
,嚴(yán)格說來它屬于實(shí)現(xiàn)細(xì)節(jié)。 用戶如果要重載內(nèi)置命名空間中的值則應(yīng)該 import
builtins
并相應(yīng)地修改該模塊中的屬性。
與一個(gè)代碼塊的執(zhí)行相關(guān)聯(lián)的內(nèi)置命名空間實(shí)際上是通過在其全局命名空間中搜索名稱 __builtins__
來找到的;這應(yīng)該是一個(gè)字典或一個(gè)模塊(在后一種情況下會使用該模塊的字典)。 默認(rèn)情況下,當(dāng)在 __main__
模塊中時(shí),__builtins__
就是內(nèi)置模塊 builtins
;當(dāng)在任何其他模塊中時(shí),__builtins__
則是 builtins
模塊自身的字典的一個(gè)別名。
4.2.4. 與動態(tài)特性的交互?
自由變量的名稱解析發(fā)生于運(yùn)行時(shí)而不是編譯時(shí)。 這意味著以下代碼將打印出 42:
i = 10
def f():
print(i)
i = 42
f()
eval()
和 exec()
函數(shù)沒有對完整環(huán)境的訪問權(quán)限來解析名稱。 名稱可以在調(diào)用者的局部和全局命名空間中被解析。 自由變量的解析不是在最近包含命名空間中,而是在全局命名空間中。 1 exec()
和 eval()
函數(shù)有可選參數(shù)用來重載全局和局部命名空間。 如果只指定一個(gè)命名空間,則它會同時(shí)作用于兩者。
4.3. 異常?
異常是中斷代碼塊的正??刂屏鞒桃员闾幚礤e(cuò)誤或其他異常條件的一種方式。 異常會在錯(cuò)誤被檢測到的位置 引發(fā),它可以被當(dāng)前包圍代碼塊或是任何直接或間接發(fā)起調(diào)用發(fā)生錯(cuò)誤的代碼塊的其他代碼塊所 處理。
Python 解析器會在檢測到運(yùn)行時(shí)錯(cuò)誤(例如零作為被除數(shù))的時(shí)候引發(fā)異常。 Python 程序也可以通過 raise
語句顯式地引發(fā)異常。 異常處理是通過 try
... except
語句來指定的。 該語句的 finally
子句可被用來指定清理代碼,它并不處理異常,而是無論之前的代碼是否發(fā)生異常都會被執(zhí)行。
Python 的錯(cuò)誤處理采用的是“終止”模型:異常處理器可以找出發(fā)生了什么問題,并在外層繼續(xù)執(zhí)行,但它不能修復(fù)錯(cuò)誤的根源并重試失敗的操作(除非通過從頂層重新進(jìn)入出錯(cuò)的代碼片段)。
當(dāng)一個(gè)異常完全未被處理時(shí),解釋器會終止程序的執(zhí)行,或者返回交互模式的主循環(huán)。 無論是哪種情況,它都會打印?;厮菪畔ⅲ鞘钱?dāng)異常為 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 的組成部分。 其內(nèi)容可能在 Python 升級到新版本時(shí)不經(jīng)警告地發(fā)生改變,不應(yīng)該被需要在多版本解釋器中運(yùn)行的代碼所依賴。
另請參看 try 語句 小節(jié)中對 try
語句的描述以及 raise 語句 小節(jié)中對 raise
語句的描述。
備注
- 1
出現(xiàn)這樣的限制是由于通過這些操作執(zhí)行的代碼在模塊被編譯的時(shí)候并不可用。