4. Execution model

4.1. 命名和绑定

名称是指对象。名称是pass名称绑定操作引入的。程序文本中名称的每次出现均指该名称在包含用途的最里面Function块中的“绑定”。

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

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

  • scope *定义块中名称的可见性。如果在块中定义了局部变量,则其范围将包括该块。如果定义出现在Function块中,则范围将扩展到定义块中包含的任何块,除非所包含的块为名称引入了不同的绑定。在类块中定义的名称范围仅限于该类块。它不会扩展到方法的代码块–包括生成器表达式,因为它们是使用函数范围实现的。这意味着以下操作将失败:
class A:
    a = 42
    b = list(a + i for i in range(10))

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

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

如果根本找不到名称,则会引发NameError异常。如果名称引用尚未绑定的局部变量,则会引发UnboundLocalError异常。 UnboundLocalErrorNameError的子类。

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

出于这种目的,在del语句中出现的目标也被认为是绑定的(尽管实际的语义是要取消绑定名称)。取消绑定范围引用的名称是非法的。编译器将报告SyntaxError

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

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

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

实际上,pass在其全局名称空间中查找名称__builtins__可以找到与代码块的执行相关联的内建名称空间。这应该是字典或模块(在后一种情况下,将使用模块的字典)。默认情况下,在main模块中时,__builtins__是内置模块builtin(注意:否)。在任何其他模块中时,__builtins__builtin模块本身的字典的别名。可以将__builtins__设置为用户创建的字典,以创建弱形式的受限执行。

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

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

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

类定义是可以使用和定义名称的可执行语句。这些参考遵循名称解析的常规规则。类定义的名称空间成为该类的属性字典。在类范围内定义的名称在方法中不可见。

4.1.1. 与动态Function的互动

在某些情况下,与包含自由变量的嵌套作用域一起使用时,Python 语句是非法的。

如果在封闭范围内引用了变量,则删除该名称是非法的。将在编译时报告错误。

如果在函数中使用了导入通配符import *的形式,并且该函数包含或是带有自由变量的嵌套块,则编译器将引发SyntaxError

如果在函数中使用exec并且该函数包含或是带有自由变量的嵌套块,则编译器将引发SyntaxError,除非 exec 明确为exec指定本地名称空间。 (换句话说,exec obj将是非法的,但exec obj in ns将是合法的.)

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

4.2. Exceptions

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

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

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

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

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

异常也可以pass字符串来标识,在这种情况下,except子句是pass对象标识来选择的。可以随同标识字符串一起引发任意值,该标识字符串可以传递给处理程序。

Note

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

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

Footnotes

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