4. 执行模型 — Python 文档
4. 执行模型
4.1. 命名和绑定
名称 指的是对象。 名称由名称绑定操作引入。 程序文本中每次出现的名称都指的是在包含使用的最内层功能块中建立的该名称的 binding。
block 是作为一个单元执行的一段 Python 程序文本。 以下是块:模块、函数体和类定义。 以交互方式键入的每个命令都是一个块。 脚本文件(作为解释器的标准输入或在解释器命令行中指定的第一个参数的文件)是一个代码块。 脚本命令(在解释器命令行上使用 '-c' 选项指定的命令)是一个代码块。 内置函数execfile()读取的文件是一个代码块。 传递给内置函数 eval() 和 exec 语句的字符串参数是一个代码块。 由内置函数 input() 读取和计算的表达式是一个代码块。
代码块在 执行帧 中执行。 帧包含一些管理信息(用于调试)并确定代码块执行完成后继续执行的位置和方式。
scope 定义块内名称的可见性。 如果在块中定义了局部变量,则其作用域包括该块。 如果定义出现在功能块中,则范围扩展到定义块中包含的任何块,除非包含的块为名称引入了不同的绑定。 类块中定义的名称范围仅限于类块; 它没有扩展到方法的代码块——这包括生成器表达式,因为它们是使用函数作用域实现的。 这意味着以下操作将失败:
class A:
a = 42
b = list(a + i for i in range(10))
在代码块中使用名称时,将使用最近的封闭范围对其进行解析。 代码块可见的所有此类范围的集合称为块的 环境 。
如果名称绑定在块中,则它是该块的局部变量。 如果名称绑定在模块级别,则它是一个全局变量。 (模块代码块的变量是局部的和全局的。)如果一个变量在代码块中使用但没有在那里定义,它是一个自由变量。
如果根本找不到名称,则会引发 NameError
异常。 如果名称引用尚未绑定的局部变量,则会引发 UnboundLocalError
异常。 UnboundLocalError
是 NameError
的子类。
以下构造绑定名称:函数的形式参数、import 语句、类和函数定义(这些绑定定义块中的类或函数名称),以及在赋值时作为标识符的目标,[ X255X]for 循环头,在 except 子句头的第二个位置或在 with 语句中的 as 之后。 形式为 from ... import *
的 import 语句绑定了导入模块中定义的所有名称,但以下划线开头的名称除外。 此表单只能在模块级别使用。
出现在 del 语句中的目标也被认为是为此目的绑定的(尽管实际语义是解除名称的绑定)。 解除封闭作用域引用的名称的绑定是非法的; 编译器将报告 SyntaxError
。
每个赋值或导入语句都出现在由类或函数定义或模块级别(顶级代码块)定义的块中。
如果名称绑定操作发生在代码块内的任何位置,则块内对该名称的所有使用都被视为对当前块的引用。 在绑定之前在块中使用名称时,这可能会导致错误。 这个规则很微妙。 Python 缺少声明并允许名称绑定操作发生在代码块中的任何位置。 代码块的局部变量可以通过扫描块的整个文本进行名称绑定操作来确定。
如果 global 语句出现在一个块中,则该语句中指定的名称的所有使用都引用该名称在顶级命名空间中的绑定。 通过搜索全局命名空间,在顶级命名空间中解析名称,即 包含代码块的模块的命名空间,以及 builtins 命名空间,模块 __builtin__ 的命名空间。 首先搜索全局命名空间。 如果在那里找不到名称,则搜索内置命名空间。 global 语句必须在名称的所有使用之前。
与代码块执行相关的内置命名空间实际上是通过在其全局命名空间中查找名称 __builtins__
来找到的; 这应该是字典或模块(在后一种情况下使用模块的字典)。 默认情况下,在__main__模块中时,__builtins__
是内置模块__builtin__(注意:没有's'); 当在任何其他模块中时, __builtins__
是 __builtin__ 模块本身的字典的别名。 __builtins__
可以设置为用户创建的字典来创建一个弱形式的受限执行。
模块的命名空间在第一次导入模块时自动创建。 脚本的主模块始终称为 __main__。
global 语句与同一块中的名称绑定操作具有相同的作用域。 如果自由变量最近的封闭作用域包含全局语句,则自由变量被视为全局变量。
类定义是可以使用和定义名称的可执行语句。 这些引用遵循名称解析的正常规则。 类定义的命名空间成为类的属性字典。 在类范围内定义的名称在方法中不可见。
4.1.1. 与动态特征的交互
在与包含自由变量的嵌套作用域结合使用时,Python 语句在多种情况下是非法的。
如果在封闭范围内引用了变量,则删除名称是非法的。 编译时会报错。
如果导入的通配符形式 — import *
— 用于函数中,并且该函数包含或者是具有自由变量的嵌套块,则编译器将引发 SyntaxError
。
如果在函数中使用 exec 并且该函数包含或是带有自由变量的嵌套块,则编译器将引发 SyntaxError
除非 exec 明确指定 的本地命名空间执行。 (换句话说,exec obj
是非法的,但 exec obj in ns
是合法的。)
eval()、execfile() 和 input() 函数和 exec 语句无法访问完整环境用于解析名称。 名称可以在调用者的本地和全局命名空间中解析。 自由变量不在最近的封闭命名空间中解析,而是在全局命名空间中解析。 1 exec 语句和 eval() 和 execfile() 函数具有可选参数来覆盖全局和本地命名空间。 如果只指定了一个命名空间,则两者都使用它。
4.2. 例外
异常是一种打破代码块正常控制流以处理错误或其他异常情况的手段。 一个例外是在检测到错误的点 raised; 它可能由周围的代码块或任何直接或间接调用发生错误的代码块的代码块 处理 。
Python 解释器在检测到运行时错误(例如被零除)时会引发异常。 Python 程序还可以使用 raise 语句显式引发异常。 异常处理程序由 try ... except 语句指定。 此类语句的 finally 子句可用于指定不处理异常的清理代码,但无论前面代码中是否发生异常,都会执行该代码。
Python 使用错误处理的“终止”模型:异常处理程序可以找出发生了什么并在外层继续执行,但它不能修复错误的原因并重试失败的操作(除非重新进入有问题的部分)顶部的代码)。
当根本没有处理异常时,解释器终止程序的执行,或返回到它的交互式主循环。 在任何一种情况下,它都会打印堆栈回溯,除非异常是 SystemExit
。
异常由类实例标识。 except 子句的选择取决于实例的类:它必须引用实例的类或其基类。 该实例可以由处理程序接收,并且可以携带有关异常情况的附加信息。
异常也可以由字符串标识,在这种情况下,except 子句由对象标识选择。 可以将任意值与可以传递给处理程序的标识字符串一起引发。
笔记
异常消息不是 Python API 的一部分。 它们的内容可能会在没有警告的情况下从 Python 的一个版本更改为下一个版本,并且不应依赖将在多个版本的解释器下运行的代码。
另请参阅 节中的 try 语句的说明raise 语句 节中的 try 语句 和 raise 语句。
脚注
- 1
- 出现此限制是因为这些操作执行的代码在编译模块时不可用。