4. Execution model

4.1. 程序的结构

Python 程序是由代码块构成的。 * block *是一段 Python 程序文本,它作为一个单元执行。以下是块:模块,函数体和类定义。交互键入的每个命令都是一个块。脚本文件(作为标准 Importing 给解释器或指定为解释器的命令行参数的文件)是代码块。脚本命令(在解释器命令行上使用-c选项指定的命令)是代码块。传递给内置函数eval()exec()的字符串参数是一个代码块。

代码块在* execution frame *中执行。框架包含一些 Management 信息(用于调试),并确定代码块执行完成后在何处以及如何 continue 执行。

4.2. 命名和绑定

4.2.1. 名称绑定

名称是指对象。名称是pass名称绑定操作引入的。

以下构造绑定名称:函数的形式参数,import语句,类和函数定义(这些绑定在定义块中的类或函数名称),以及如果是在赋值,for循环头中或as之后出现的则为标识符的目标在with语句或except子句中。格式为from ... import *import语句绑定在导入模块中定义的所有名称,但下划线开头的名称除外。该表格只能在模块级别使用。

出于这种目的,在del语句中出现的目标也被认为是绑定的(尽管实际的语义是要取消绑定名称)。

每个赋值或导入语句都出现在由类或函数定义定义的块内或模块级别(顶层代码块)上。

如果名称绑定在块中,则除非语句为nonlocalglobal,否则它是该块的局部变量。如果名称在模块级别绑定,则它是全局变量。 (模块代码块的变量是局部变量和全局变量.)如果在代码块中使用了变量但未在其中定义,则它是* free 变量*。

程序文本中每次出现的名称都表示pass以下名称解析规则构建的名称“绑定”。

4.2.2. 名称解析

  • scope *定义块中名称的可见性。如果在块中定义了局部变量,则其范围将包括该块。如果定义出现在Function块中,则范围将扩展到定义块中包含的任何块,除非所包含的块为名称引入了不同的绑定。

在代码块中使用名称时,将使用最近的封闭范围来解析它。代码块可见的所有此类范围的集合称为该块的* environment *。

如果根本找不到名称,则会引发NameError异常。如果当前作用域是函数作用域,并且该名称引用的是尚未使用该名称绑定到值的局部变量,则会引发UnboundLocalError异常。 UnboundLocalErrorNameError的子类。

如果名称绑定操作发生在代码块内的任何位置,则该块内对名称的所有使用都将视为对当前块的引用。在绑定名称之前在块中使用名称时,这可能导致错误。这条规则很微妙。 Python 缺少语句,并且允许名称绑定操作发生在代码块内的任何位置。可以pass扫描代码块的整个文本以进行名称绑定操作来确定代码块的局部变量。

如果global语句出现在块内,则该语句中指定的名称的所有使用均指该名称在顶级名称空间中的绑定。pass搜索全局名称空间(即包含代码块的模块的名称空间)和内建名称空间(模块builtins的名称空间),可以在顶级名称空间中解析名称。首先搜索全局名称空间。如果未在此处找到名称,则搜索内建名称空间。 global语句必须在名称的所有用法之前。

global语句的作用域与同一块中的名称绑定操作相同。如果最接近的自由变量包围范围包含全局语句,则将自由变量视为全局语句。

nonlocal语句使对应的名称引用最近的封闭函数作用域中的先前绑定的变量。如果给定名称在任何封闭函数作用域中不存在,则在编译时引发SyntaxError

首次导入模块时,将自动创建模块的名称空间。脚本的主要模块始终称为main

在名称解析的上下文中,类定义块和exec()eval()的参数是特殊的。类定义是可以使用和定义名称的可执行语句。这些引用遵循通常的名称解析规则,但在全局命名空间中查找未绑定的局部变量。类定义的名称空间成为该类的属性字典。在类块中定义的名称范围仅限于该类块。它不会扩展到方法的代码块–包括理解和生成器表达式,因为它们是使用函数范围实现的。这意味着以下操作将失败:

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

4.2.3. 内建函数和受限执行

CPython 实现细节: 用户不应触摸__builtins__;严格来说,这是一个实现细节。希望覆盖内建命名空间中的值的用户应import builtins模块并适当修改其属性。

实际上,pass在其全局名称空间中查找名称__builtins__可以找到与代码块的执行相关联的内建名称空间。这应该是字典或模块(在后一种情况下,将使用模块的字典)。默认情况下,在main模块中时,__builtins__是内置模块builtins;在任何其他模块中时,__builtins__builtins模块本身的字典的别名。

4.2.4. 与动态Function的互动

可用变量的名称解析在运行时发生,而不是在编译时发生。这意味着以下代码将打印 42:

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

eval()exec()函数无法访问用于解析名称的完整环境。名称可以在调用方的本地和全局名称空间中解析。可用变量不是在最接近的封闭名称空间中解析,而是在全局名称空间中解析。 [1] exec()eval()函数具有可选参数,以覆盖全局和本地名称空间。如果仅指定一个名称空间,则将两者同时使用。

4.3. Exceptions

异常是一种 break 常规的代码块控制流程以便处理错误或其他特殊情况的方法。在检测到错误的地方“引发”异常;它可以由周围的代码块或直接或间接调用发生错误的代码块的任何代码块“处理”。

当检测到运行时错误(例如零除)时,Python 解释器会引发异常。 Python 程序还可以使用raise语句显式引发异常。异常处理程序passtryexcept语句指定。此类语句的finally子句可用于指定不处理该异常的清理代码,但是无论先前代码中是否发生异常,该清理代码都会执行。

Python 使用错误处理的“终止”模型:异常处理程序可以找出发生了什么并 continue 在外部执行,但是它无法修复错误原因并重试失败的操作(除非重新 Importing 有问题的部分)代码的顶部)。

当根本没有处理异常时,解释器将终止程序的执行,或返回其交互式主循环。无论哪种情况,它都将打印堆栈回溯,除非SystemExit除外。

异常由类实例标识。 except子句的选择取决于实例的类:它必须引用实例的类或其 Base Class。该实例可以由处理程序接收,并且可以携带有关异常情况的其他信息。

Note

异常消息不是 Python API 的一部分。它们的内容可能在没有警告的情况下从一个版本的 Python 更改为另一个版本,并且不应由将在多个版本的解释器下运行的代码所依赖。

另请参见try 语句节中的try语句和raise语句节中的raise语句的描述。

Footnotes

  • [1]
    • 出现此限制的原因是,在编译模块时,这些操作执行的代码不可用。