7. 简单语句

简单语句由一个单独的逻辑行构成。 多条简单语句可以存在于同一行内并以分号分隔。 简单语句的句法为:

simple_stmt ::=  expression_stmt
                 | assert_stmt
                 | assignment_stmt
                 | augmented_assignment_stmt
                 | annotated_assignment_stmt
                 | pass_stmt
                 | del_stmt
                 | return_stmt
                 | yield_stmt
                 | raise_stmt
                 | break_stmt
                 | continue_stmt
                 | import_stmt
                 | future_stmt
                 | global_stmt
                 | nonlocal_stmt

7.1. 表达式语句

表达式语句用于计算和写入值(大多是在交互模式下),或者(通常情况)调用一个过程 (过程就是不返回有意义结果的函数;在 Python 中,过程的返回值为 None)。 表达式语句的其他使用方式也是允许且有特定用处的。 表达式语句的句法为:

expression_stmt ::=  starred_expression

表达式语句会对指定的表达式列表(也可能为单一表达式)进行求值。

在交互模式下,如果结果值不为 None,它会通过内置的 repr() 函数转换为一个字符串,该结果字符串将以单独一行的形式写入标准输出(例外情况是如果结果为 None,则该过程调用不产生任何输出。)

7.2. 赋值语句

赋值语句用于将名称(重)绑定到特定值,以及修改属性或可变对象的成员项:

assignment_stmt ::=  (target_list "=")+ (starred_expression | yield_expression)
target_list     ::=  target ("," target)* [","]
target          ::=  identifier
                     | "(" [target_list] ")"
                     | "[" [target_list] "]"
                     | attributeref
                     | subscription
                     | slicing
                     | "*" target

(请参阅 原型 一节了解 属性引用, 抽取切片 的句法定义。)

赋值语句会对指定的表达式列表进行求值(注意这可能为单一表达式或是由逗号分隔的列表,后者将产生一个元组)并将单一结果对象从左至右逐个赋值给目标列表。

赋值是根据目标(列表)的格式递归地定义的。 当目标为一个可变对象(属性引用、抽取或切片)的组成部分时,该可变对象必须最终执行赋值并决定其有效性,如果赋值操作不可接受也可能引发异常。 各种类型可用的规则和引发的异常通过对象类型的定义给出(参见 标准类型层级结构 一节)。

对象赋值的目标对象可以包含于圆括号或方括号内,具体操作按以下方式递归地定义。

  • 如果目标列表为后面不带逗号、可以包含于圆括号内的单一目标,则将对象赋值给该目标。

  • 否则:该对象必须为具有与目标列表相同项数的可迭代对象,这些项将按从左至右的顺序被赋值给对应的目标。

    • 如果目标列表包含一个带有星号前缀的目标,这称为“加星”目标:则该对象至少必须为与目标列表项数减一相同项数的可迭代对象。 该可迭代对象前面的项将按从左至右的顺序被赋值给加星目标之前的目标。 该可迭代对象末尾的项将被赋值给加星目标之后的目标。 然后该可迭代对象中剩余项的列表将被赋值给加星目标(该列表可以为空)。

    • 否则:该对象必须为具有与目标列表相同项数的可迭代对象,这些项将按从左至右的顺序被赋值给对应的目标。

对象赋值给单个目标的操作按以下方式递归地定义。

  • 如果目标为标识符(名称):

    • 如果该名称未出现于当前代码块的 globalnonlocal 语句中:该名称将被绑定到当前局部命名空间的对象。

    • 否则:该名称将被分别绑定到全局命名空间或由 nonlocal 所确定的外层命名空间的对象。

    如果该名称已经被绑定则将被重新绑定。 这可能导致之前被绑定到该名称的对象的引用计数变为零,造成该对象进入释放过程并调用其析构器(如果存在)。

  • 如果该对象为属性引用:引用中的原型表达式会被求值。 它应该产生一个具有可赋值属性的对象;否则将引发 TypeError。 该对象会被要求将可赋值对象赋值给指定的属性;如果它无法执行赋值,则会引发异常 (通常应为 AttributeError 但并不强制要求)。

    注意:如果该对象为类实例并且属性引用在赋值运算符的两侧都出现,则右侧表达式 a.x 可以访问实例属性或(如果实例属性不存在)类属性。 左侧目标 a.x 将总是设定为实例属性,并在必要时创建该实例属性。 因此,a.x 的两次出现不一定指向相同的属性:如果右侧表达式指向一个类属性,则左侧表达式会创建一个新的实例属性作为赋值的目标:

    class Cls:
        x = 3             # class variable
    inst = Cls()
    inst.x = inst.x + 1   # writes inst.x as 4 leaving Cls.x as 3
    

    此描述不一定作用于描述器属性,例如通过 property() 创建的特征属性。

  • 如果目标为一个抽取项:引用中的原型表达式会被求值。 它应当产生一个可变序列对象(例如列表)或一个映射对象(例如字典)。 接下来,该抽取表达式会被求值。

    如果原型为一个可变序列对象(例如列表),抽取应产生一个整数。 如其为负值,则再加上序列长度。 结果值必须为一个小于序列长度的非负整数,序列将把被赋值对象赋值给该整数指定索引号的项。 如果索引超出范围,将会引发 IndexError (给被抽取序列赋值不能向列表添加新项)。

    如果原型为一个映射对象(例如字典),抽取必须具有与该映射的键类型相兼容的类型,然后映射中会创建一个将抽取映射到被赋值对象的键/值对。 这可以是替换一个现有键/值对并保持相同键值,也可以是插入一个新键/值对(如果具有相同值的键不存在)。

    对于用户定义对象,会调用 __setitem__() 方法并附带适当的参数。

  • 如果目标为一个切片:引用中的原型表达式会被求值。 它应当产生一个可变序列对象(例如列表)。 被赋值对象应当是一个相同类型的序列对象。 接下来,下界与上界表达式如果存在的话将被求值;默认值分别为零和序列长度。 上下边界值应当为整数。 如果某一边界为负值,则会加上序列长度。 求出的边界会被裁剪至介于零和序列长度的开区间中。 最后,将要求序列对象以被赋值序列的项替换该切片。 切片的长度可能与被赋值序列的长度不同,这会在目标序列允许的情况下改变目标序列的长度。

CPython implementation detail: 在当前实现中,目标的句法被当作与表达式的句法相同,无效的句法会在代码生成阶段被拒绝,导致不太详细的错误信息。

虽然赋值的定义意味着左手边与右手边的重叠是“同时”进行的(例如 a, b = b, a 会交换两个变量的值),但在赋值给变量的多项集 之内 的重叠是从左至右进行的,这有时会令人混淆。 例如,以下程序将会打印出 [0, 2]:

x = [0, 1]
i = 0
i, x[i] = 1, 2         # i is updated, then x[i] is updated
print(x)

也參考

PEP 3132 - 扩展的可迭代对象拆包

*target 特性的规范说明。

7.2.1. 增强赋值语句

增强赋值语句就是在单个语句中将二元运算和赋值语句合为一体:

augmented_assignment_stmt ::=  augtarget augop (expression_list | yield_expression)
augtarget                 ::=  identifier | attributeref | subscription | slicing
augop                     ::=  "+=" | "-=" | "*=" | "@=" | "/=" | "//=" | "%=" | "**="
                               | ">>=" | "<<=" | "&=" | "^=" | "|="

(请参阅 原型 一节了解最后三种符号的句法定义。)

增强赋值语句将对目标和表达式列表求值(与普通赋值语句不同的是,前者不能为可迭代对象拆包),对两个操作数相应类型的赋值执行指定的二元运算,并将结果赋值给原始目标。 目标仅会被求值一次。

增强赋值语句例如 x += 1 可以改写为 x = x + 1 获得类似但并非完全等价的效果。 在增强赋值的版本中,x 仅会被求值一次。 而且,在可能的情况下,实际的运算是 原地 执行的,也就是说并不是创建一个新对象并将其赋值给目标,而是直接修改原对象。

不同于普通赋值,增强赋值会在对右手边求值 之前 对左手边求值。 例如,a[i] += f(x) 首先查找 a[i],然后对 f(x) 求值并执行加法操作,最后将结果写回到 a[i]

除了在单个语句中赋值给元组和多个目标的例外情况,增强赋值语句的赋值操作处理方式与普通赋值相同。 类似地,除了可能存在 原地 操作行为的例外情况,增强赋值语句执行的二元运算也与普通二元运算相同。

对于属性引用类目标,针对常规赋值的 关于类和实例属性的警告 也同样适用。

7.2.2. 带标注的赋值语句

Annotation assignment is the combination, in a single statement, of a variable or attribute annotation and an optional assignment statement:

annotated_assignment_stmt ::=  augtarget ":" expression ["=" expression]

与普通 赋值语句 的差别在于仅有单个目标且仅有单个右手边的值才被允许。

对于将简单名称作为赋值目标的情况,如果是在类或模块作用域中,标注会被求值并存入一个特殊的类或模块属性 __annotations__ 中,这是一个将变量名称(如为私有会被移除)映射到被求值标注的字典。 此属性为可写并且在类或模块体开始执行时如果静态地发现标注就会自动创建。

对于将表达式作为赋值目标的情况,如果是在类或模块作用域中,标注会被求值,但不会保存。

如果一个名称在函数作用域内被标注,则该名称为该作用域的局部变量。 标注绝不会在函数作用域内被求值和保存。

如果存在右手边,带标注的赋值会在对标注求值之前(如果适用)执行实际的赋值。 如果用作表达式目标的右手边不存在,则解释器会对目标求值,但最后的 __setitem__()__setattr__() 调用除外。

也參考

PEP 526 - 变量标注的语法

该提议增加了标注变量(也包括类变量和实例变量)类型的语法,而不再是通过注释来进行表达。

PEP 484 - 类型提示

该提议增加了 typing 模块以便为类型标注提供标准句法,可被静态分析工具和 IDE 所使用。

7.3. The assert statement

assert 语句是在程序中插入调试性断言的简便方式:

assert_stmt ::=  "assert" expression ["," expression]

简单形式 assert expression 等价于

if __debug__:
    if not expression: raise AssertionError

扩展形式 assert expression1, expression2 等价于

if __debug__:
    if not expression1: raise AssertionError(expression2)

以上等价形式假定 __debug__AssertionError 指向具有指定名称的内置变量。 在当前实现中,内置变量 __debug__ 在正常情况下为 True,在请求优化时为 False (对应命令行选项为 -O)。 如果在编译时请求优化,当前代码生成器不会为 assert 语句发出任何代码。 请注意不必在错误信息中包含失败表达式的源代码;它会被作为栈追踪的一部分被显示。

赋值给 __debug__ 是非法的。 该内置变量的值会在解释器启动时确定。

7.4. The pass statement

pass_stmt ::=  "pass"

pass 是一个空操作 — 当它被执行时,什么都不发生。 它适合当语法上需要一条语句但并不需要执行任何代码时用来临时占位,例如:

def f(arg): pass    # a function that does nothing (yet)

class C: pass       # a class with no methods (yet)

7.5. The del statement

del_stmt ::=  "del" target_list

删除是递归定义的,与赋值的定义方式非常类似。 此处不再详细说明,只给出一些提示。

目标列表的删除将从左至右递归地删除每一个目标。

名称的删除将从局部或全局命名空间中移除该名称的绑定,具体作用域的确定是看该名称是否有在同一代码块的 global 语句中出现。 如果该名称未被绑定,将会引发 NameError

属性引用、抽取和切片的删除会被传递给相应的原型对象;删除一个切片基本等价于赋值为一个右侧类型的空切片(但即便这一点也是由切片对象决定的)。

3.2 版更變: 在之前版本中,如果一个名称作为被嵌套代码块中的自由变量出现,则将其从局部命名空间中删除是非法的。

7.6. The return statement

return_stmt ::=  "return" [expression_list]

return 在语法上只会出现于函数定义所嵌套的代码,不会出现于类定义所嵌套的代码。

如果提供了表达式列表,它将被求值,否则以 None 替代。

return 会离开当前函数调用,并以表达式列表 (或 None) 作为返回值。

When return passes control out of a try statement with a finally clause, that finally clause is executed before really leaving the function.

在一个生成器函数中,return 语句表示生成器已完成并将导致 StopIteration 被引发。 返回值(如果有的话)会被当作一个参数用来构建 StopIteration 并成为 StopIteration.value 属性。

In an asynchronous generator function, an empty return statement indicates that the asynchronous generator is done and will cause StopAsyncIteration to be raised. A non-empty return statement is a syntax error in an asynchronous generator function.

7.7. The yield statement

yield_stmt ::=  yield_expression

yield 语句在语义上等同于 yield 表达式。 yield 语句可用来省略在使用等效的 yield 表达式语句时所必须的圆括号。 例如,以下 yield 语句

yield <expr>
yield from <expr>

等同于以下 yield 表达式语句

(yield <expr>)
(yield from <expr>)

yield 表达式和语句仅在定义 generator 函数时使用,并且仅被用于生成器函数的函数体内部。 在函数定义中使用 yield 就足以使得该定义创建的是生成器函数而非普通函数。

有关 yield 语义的完整细节请参看 yield 表达式 一节。

7.8. The raise statement

raise_stmt ::=  "raise" [expression ["from" expression]]

如果不带表达式,raise 会重新引发当前作用域内最后一个激活的异常。 如果当前作用域内没有激活的异常,将会引发 RuntimeError 来提示错误。

否则的话,raise 会将第一个表达式求值为异常对象。 它必须为 BaseException 的子类或实例。 如果它是一个类,当需要时会通过不带参数地实例化该类来获得异常的实例。

异常的 类型 为异常实例的类, 为实例本身。

当异常被引发时通常会自动创建一个回溯对象并将其关联到可写的 __traceback__ 属性。 你可以创建一个异常并同时使用 with_traceback() 异常方法(该方法将返回同一异常实例,并将回溯对象设为其参数)设置自己的回溯,就像这样:

raise Exception("foo occurred").with_traceback(tracebackobj)

from 子句用于异常串连:如果有该子句,则第二个 表达式 必须为另一个异常或实例,它将作为可写的 __cause__ 属性被关联到所引发的异常。 如果引发的异常未被处理,两个异常都将被打印出来:

>>> try:
...     print(1 / 0)
... except Exception as exc:
...     raise RuntimeError("Something bad happened") from exc
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: division by zero

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
RuntimeError: Something bad happened

如果一个异常在异常处理器或 finally clause: 中被引发,类似的机制会隐式地发挥作用,之前的异常将被关联到新异常的 __context__ 属性:

>>> try:
...     print(1 / 0)
... except:
...     raise RuntimeError("Something bad happened")
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: division by zero

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
RuntimeError: Something bad happened

异常串连可通过在 from 子句中指定 None 来显式地加以抑制:

>>> try:
...     print(1 / 0)
... except:
...     raise RuntimeError("Something bad happened") from None
...
Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
RuntimeError: Something bad happened

有关异常的更多信息可在 异常 一节查看,有关处理异常的信息可在 The try statement 一节查看。

3.3 版更變: None 现在允许被用作 raise X from Y 中的 Y

3.3 版新加入: 使用 __suppress_context__ 属性来抑制异常上下文的自动显示。

7.9. The break statement

break_stmt ::=  "break"

break 在语法上只会出现于 forwhile 循环所嵌套的代码,但不会出现于该循环内部的函数或类定义所嵌套的代码。

It terminates the nearest enclosing loop, skipping the optional else clause if the loop has one.

如果一个 for 循环被 break 所终结,该循环的控制目标会保持其当前值。

When break passes control out of a try statement with a finally clause, that finally clause is executed before really leaving the loop.

7.10. The continue statement

continue_stmt ::=  "continue"

continue 在语法上只会出现于 forwhile 循环所嵌套的代码,但不会出现于该循环内部的函数或类定义或者 finally 子句所嵌套的代码。 它会继续执行最近的外层循环的下一个轮次。

When continue passes control out of a try statement with a finally clause, that finally clause is executed before really starting the next loop cycle.

7.11. The import statement

import_stmt     ::=  "import" module ["as" identifier] ("," module ["as" identifier])*
                     | "from" relative_module "import" identifier ["as" identifier]
                     ("," identifier ["as" identifier])*
                     | "from" relative_module "import" "(" identifier ["as" identifier]
                     ("," identifier ["as" identifier])* [","] ")"
                     | "from" module "import" "*"
module          ::=  (identifier ".")* identifier
relative_module ::=  "."* module | "."+

基本的 import 语句(不带 from 子句)会分两步执行:

  1. 查找一个模块,如果有必要还会加载并初始化模块。

  2. 在局部命名空间中为 import 语句发生位置所处的作用域定义一个或多个名称。

当语句包含多个子句(由逗号分隔)时这两个步骤将对每个子句分别执行,如同这些子句被分成独立的 import 语句一样。

第一个步骤即查找和加载模块的详情 导入系统 一节中有更详细的描述,其中也描述了可被导入的多种类型的包和模块,以及可用于定制导入系统的所有钩子对象。 请注意这一步如果失败,则可能说明模块无法找到,或者 是在初始化模块,包括执行模块代码期间发生了错误。

如果成功获取到请求的模块,则可以通过以下三种方式一之在局部命名空间中使用它:

  • If the module name is followed by as, then the name following as is bound directly to the imported module.

  • 如果没有指定其他名称,且被导入的模块为最高层级模块,则模块的名称将被绑定到局部命名空间作为对所导入模块的引用。

  • 如果被导入的模块 不是 最高层级模块,则包含该模块的最高层级包的名称将被绑定到局部命名空间作为对该最高层级包的引用。 所导入的模块必须使用其完整限定名称来访问而不能直接访问。

from 形式使用的过程略微繁复一些:

  1. 查找 from 子句中指定的模块,如有必要还会加载并初始化模块;

  2. 对于 import 子句中指定的每个标识符:

    1. 检查被导入模块是否有该名称的属性

    2. 如果没有,尝试导入具有该名称的子模块,然后再次检查被导入模块是否有该属性

    3. 如果未找到该属性,则引发 ImportError

    4. otherwise, a reference to that value is stored in the local namespace, using the name in the as clause if it is present, otherwise using the attribute name

示例:

import foo                 # foo imported and bound locally
import foo.bar.baz         # foo.bar.baz imported, foo bound locally
import foo.bar.baz as fbb  # foo.bar.baz imported and bound as fbb
from foo.bar import baz    # foo.bar.baz imported and bound as baz
from foo import attr       # foo imported and foo.attr bound as attr

如果标识符列表改为一个星号 ('*'),则在模块中定义的全部公有名称都将按 import 语句所在的作用域被绑定到局部命名空间。

一个模块所定义的 公有名称 是由在模块的命名空间中检测一个名为 __all__ 的变量来确定的;如果有定义,它必须是一个字符串列表,其中的项为该模块所定义或导入的名称。 在 __all__ 中所给出的名称都会被视为公有并且应当存在。 如果 __all__ 没有被定义,则公有名称的集合将包含在模块的命名空间中找到的所有不以下划线字符 ('_') 打头的名称。 __all__ 应当包括整个公有 API。 它的目标是避免意外地导出不属于 API 的一部分的项(例如在模块内部被导入和使用的库模块)。

通配符形式的导入 — from module import * — 仅在模块层级上被允许。 尝试在类或函数定义中使用它将引发 SyntaxError

When specifying what module to import you do not have to specify the absolute name of the module. When a module or package is contained within another package it is possible to make a relative import within the same top package without having to mention the package name. By using leading dots in the specified module or package after from you can specify how high to traverse up the current package hierarchy without specifying exact names. One leading dot means the current package where the module making the import exists. Two dots means up one package level. Three dots is up two levels, etc. So if you execute from . import mod from a module in the pkg package then you will end up importing pkg.mod. If you execute from ..subpkg2 import mod from within pkg.subpkg1 you will import pkg.subpkg2.mod. The specification for relative imports is contained within PEP 328.

importlib.import_module() 被提供用来为动态地确定要导入模块的应用提供支持。

7.11.1. future 语句

future 语句 是一种针对编译器的指令,指明某个特定模块应当使用在特定的未来某个 Python 发行版中成为标准特性的语法或语义。

future 语句的目的是使得向在语言中引入了不兼容改变的 Python 未来版本的迁移更为容易。 它允许基于每个模块在某种新特性成为标准之前的发行版中使用该特性。

future_stmt ::=  "from" "__future__" "import" feature ["as" identifier]
                 ("," feature ["as" identifier])*
                 | "from" "__future__" "import" "(" feature ["as" identifier]
                 ("," feature ["as" identifier])* [","] ")"
feature     ::=  identifier

future 语句必须在靠近模块开头的位置出现。 可以出现在 future 语句之前行只有:

  • 模块的文档字符串(如果存在),

  • 注释,

  • 空行,以及

  • 其他 future 语句。

The features recognized by Python 3.0 are absolute_import, division, generators, unicode_literals, print_function, nested_scopes and with_statement. They are all redundant because they are always enabled, and only kept for backwards compatibility.

future 语句在编译时会被识别并做特殊对待:对核心构造语义的改变常常是通过生成不同的代码来实现。 新的特性甚至可能会引入新的不兼容语法(例如新的保留字),在这种情况下编译器可能需要以不同的方式来解析模块。 这样的决定不能推迟到运行时方才作出。

对于任何给定的发布版本,编译器要知道哪些特性名称已被定义,如果某个 future 语句包含未知的特性则会引发编译时错误。

直接运行时的语义与任何 import 语句相同:存在一个后文将详细说明的标准模块 __future__,它会在执行 future 语句时以通常的方式被导入。

相应的运行时语义取决于 future 语句所启用的指定特性。

请注意以下语句没有任何特别之处:

import __future__ [as name]

这并非 future 语句;它只是一条没有特殊语义或语法限制的普通 import 语句。

在默认情况下,通过对Code compiled by calls to the 内置函数 exec()compile() 的调用所编译的代码如果出现于一个包含有 future 语句的模块 M 之中,就会使用 future 语句所关联的语法和语义。 此行为可以通过 compile() 的可选参数加以控制 — 请参阅该函数的文档以了解详情。

在交互式解释器提示符中键入的 future 语句将在解释器会话此后的交互中有效。 如果一个解释器的启动使用了 -i 选项启动,并传入了一个脚本名称来执行,且该脚本包含 future 语句,它将在交互式会话开始执行脚本之后保持有效。

也參考

PEP 236 - 回到 __future__

有关 __future__ 机制的最初提议。

7.12. The global statement

global_stmt ::=  "global" identifier ("," identifier)*

The global statement is a declaration which holds for the entire current code block. It means that the listed identifiers are to be interpreted as globals. It would be impossible to assign to a global variable without global, although free variables may refer to globals without being declared global.

Names listed in a global statement must not be used in the same code block textually preceding that global statement.

global 语句中列出的名称不能被定义为形式参数,也不能在 for 循环的控制目标、class 定义、函数定义、import 语句或变量标注中定义。

CPython implementation detail: 当前的实现并未强制要求所有的上述限制,但程序不应当滥用这样的自由,因为未来的实现可能会改为强制要求,并静默地改变程序的含义。

Programmer’s note: global is a directive to the parser. It applies only to code parsed at the same time as the global statement. In particular, a global statement contained in a string or code object supplied to the built-in exec() function does not affect the code block containing the function call, and code contained in such a string is unaffected by global statements in the code containing the function call. The same applies to the eval() and compile() functions.

7.13. The nonlocal statement

nonlocal_stmt ::=  "nonlocal" identifier ("," identifier)*

nonlocal 语句会使得所列出的名称指向之前在最近的包含作用域中绑定的除全局变量以外的变量。 这种功能很重要,因为绑定的默认行为是先搜索局部命名空间。 这个语句允许被封装的代码重新绑定局部作用域以外且非全局(模块)作用域当中的变量。

global 语句中列出的名称不同,nonlocal 语句中列出的名称必须指向之前存在于包含作用域之中的绑定(在这个应当用来创建新绑定的作用域不能被无歧义地确定)。

nonlocal 语句中列出的名称不得与之前存在于局部作用域中的绑定相冲突。

也參考

PEP 3104 - 访问外层作用域中的名称

有关 nonlocal 语句的规范说明。