7. Simple statements

一个简单的语句包含在单个逻辑行中。在用分号分隔的一行上可能会出现几个简单的语句。简单语句的语法为:

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. 表达 Statements

Expression 语句(通常是交互式地)用于计算和写入值,或(通常)用于调用过程(一个不返回有意义结果的函数;在 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

(有关* attributeref subscription slicing *的语法定义,请参见Primaries节。)

赋值语句评估表达式列表(请记住,它可以是单个表达式或逗号分隔的列表,后者产生一个 Tuples),并将单个结果对象从左到右分配给每个目标列表。

递归定义取决于目标(列表)的形式。当目标是可变对象(属性引用,订阅或切片)的一部分时,可变对象必须finally执行分配并决定其有效性,如果分配是不可接受的,则可能引发异常。对象类型的定义给出了各种类型遵守的规则和引发的异常(请参见标准类型层次结构部分)。

将对象分配给目标列表的方法如下(可选地用括号或方括号括起来):

  • 如果目标列表是一个没有尾随逗号的单个目标(可以选择在括号中),则将对象分配给该目标。

  • 否则:该对象必须是可迭代的,并且具有与目标列表中的目标相同数量的项目,并且这些项目从左到右分配给相应的目标。

  • 如果目标列表包含一个标有星号的目标,称为“已加星标”目标:该对象必须是可迭代的,且其至少与目标列表中的目标数量一样多(减一)。可迭代对象的第一项从左到右分配给已加星标目标之前的目标。可迭代对象的最后一项将分配给已加星标的目标。然后,将可迭代项中其余项目的列表分配给已加星标的目标(列表可以为空)。

    • 否则:该对象必须是可迭代的,并且具有与目标列表中的目标相同数量的项目,并且这些项目从左到右分配给相应的目标。

将对象分配给单个目标的定义如下。

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

  • 如果该名称未出现在当前代码块的globalnonlocal语句中:该名称将绑定到当前本地名称空间中的对象。

    • 否则:名称分别绑定到全局名称空间或nonlocal确定的外部名称空间中的对象。

如果已经绑定,则名称为反弹。这可能导致先前绑定到名称的对象的引用计数达到零,从而导致该对象被释放并调用其析构函数(如果有的话)。

  • 如果目标是属性引用:评估引用中的主表达式。它应该产生一个具有可分配属性的对象;如果不是这种情况,将引发TypeError。然后要求该对象将分配的对象分配给给定的属性;如果无法执行分配,则会引发异常(通常但不一定是AttributeError)。

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

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

此描述不一定适用于 Descriptors 属性,例如property()创建的属性。

  • 如果目标是预订:评估引用中的主表达式。它应该产生可变的序列对象(例如列表)或 Map 对象(例如字典)。接下来,评估下标表达式。

如果主要对象是可变序列对象(例如列表),则下标必须产生一个整数。如果为负,则将序列的长度添加到其中。结果值必须是小于序列长度的非负整数,并且要求序列将具有该索引的已分配对象分配给其对象。如果索引超出范围,则将引发IndexError(分配给下标的序列不能将新项目添加到列表中)。

如果主要对象是 Map 对象(例如字典),则下标必须具有与 Map 键类型兼容的类型,然后要求 Map 创建一个将下标 Map 到指定对象的键/基准对。这可以用相同的键值替换现有的键/值对,也可以插入新的键/值对(如果不存在具有相同值的键)。

对于用户定义的对象,使用适当的参数调用setitem()方法。

  • 如果目标是切片:评估引用中的主表达式。它应该产生一个可变的序列对象(例如列表)。分配的对象应该是相同类型的序列对象。接下来,在存在下限和上限表达式的情况下对它们进行求值;默认值为零,序列的长度。边界的值应为整数。如果任一边界为负,则将序列的长度添加到其中。将结果边界裁剪为介于零和序列长度之间(包括端值)。最后,要求序列对象用指定序列的项目替换切片。切片的长度可能与分配的序列的长度不同,因此,如果目标序列允许,则可以更改目标序列的长度。

CPython 实现细节: 在当前实现中,目标的语法被认为与表达式的语法相同,并且无效的语法在代码生成阶段被拒绝,从而导致不太详细的错误消息。

尽管赋值的定义意味着左侧和右侧之间的重叠是“同时的”(例如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)

See also

  • PEP 3132-扩展的可迭代拆包

  • *targetFunction的规范。

7.2.1. 参数赋值语句

扩展赋值是二进制操作和赋值语句在单个语句中的组合:

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

(有关最后三个符号的语法定义,请参见Primaries节。)

扩展赋值评估目标(与普通赋值语句不同,不能解包)和表达式列表,对两个操作数执行特定于赋值类型的二进制运算,然后将结果赋值给原始目标。该目标仅评估一次。

可以将增强的赋值表达式x += 1重写为x = x + 1,以实现相似但不完全相等的效果。在增强版本中,x仅被评估一次。另外,在可能的情况下,实际操作会“就地”执行,这意味着不是创建新对象并将其分配给目标,而是修改了旧对象。

与普通作业不同,增强型作业在评估右侧之前先评估左侧。例如,a[i] += f(x)首先查询a[i],然后评估f(x)并执行加法,最后将结果写回到a[i]

除了在单个语句中分配给 Tuples 和多个目标外,由扩展赋值语句完成的赋值与普通赋值的处理方式相同。类似地,除了可能的“就地”行为外,pass扩充分配执行的二进制操作与正常的二进制操作相同。

对于作为属性引用的目标,与常规分配相同的关于类和实例属性的警告适用。

7.2.2. 带 Comments 的赋值语句

Annotation赋值是变量或属性 Comments 和可选赋值语句在单个语句中的组合:

annotated_assignment_stmt ::=  augtarget ":" expression
                               ["=" (starred_expression | yield_expression)]

与常规Assignment statements的区别在于仅允许一个目标。

对于作为分配目标的简单名称,如果在类或模块范围内,则对 Comments 进行评估并将其存储在特殊的类或模块属性__annotations__中,该属性是从变量名(如果私有则乱码)Map 到评估的 Comments 的字典 Map。如果可以静态找到 Comments,则该属性是可写的,并且在类或模块主体执行开始时自动创建。

对于作为赋值目标的表达式,如果在类或模块范围内,则对 Comments 进行评估,但不进行存储。

如果在函数作用域中标注了名称,则该名称在该作用域中是本地的。Comments 永远不会求值并存储在函数作用域中。

如果存在右侧,则带 Comments 的分配会在评估 Comments(如果适用)之前执行实际分配。如果表达目标的右侧不存在,则解释器将评估目标,最后一次setitem()setattr()调用除外。

See also

  • PEP 526-变量 Comments 的语法

  • 该提案添加了用于 Comments 变量类型(包括类变量和实例变量)的语法,而不是pass Comments 来表达它们。

  • PEP 484-Importing 提示

  • 提案中添加了typing模块,以为类型 Comments 提供标准语法,该 Comments 可在静态分析工具和 IDE 中使用。

在版本 3.8 中进行了更改:现在,带 Comments 的赋值允许在右侧使用与常规赋值相同的表达式。以前,某些表达式(例如未加括号的 Tuples 表达式)导致语法错误。

7.3. 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)

这些等价假定debugAssertionError引用具有这些名称的内置变量。在当前实现中,内置变量debug在正常情况下为True,当请求优化时为False(命令行选项-O)。当在编译时请求优化时,当前代码生成器不会为 assert 语句生成任何代码。注意,没有必要在错误消息中包含失败的表达式的源代码。它会显示为堆栈跟踪的一部分。

分配给debug是非法的。内置变量的值在解释器启动时确定。

7.4. pass 语句

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. del 语句

del_stmt ::=  "del" target_list

递归定义删除与定义分配的方式非常相似。这里有一些提示,而不是详细说明。

目标列表的删除会从左到右递归删除每个目标。

名称的删除会从本地或全局名称空间中删除该名称的绑定,具体取决于该名称是否出现在同一代码块的global语句中。如果名称未绑定,则会引发NameError异常。

属性引用,订阅和切片的删除被传递给所涉及的主要对象。切片的删除通常等效于分配正确类型的空切片(但即使是由切片的对象确定)。

在版本 3.2 中进行了更改:以前,如果它在嵌套块中作为自由变量出现,则从本地名称空间中删除名称是非法的。

7.6. return语句

return_stmt ::=  "return" [expression_list]

return只能在语法上嵌套在函数定义中,而不能在嵌套的类定义中。

如果存在表达式列表,则将对其求值,否则将替换None

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

returnpassfinally子句将控制权从try语句中移出时,该finally子句在 true 离开函数之前被执行。

在生成器函数中,return语句指示生成器已完成,并且将引发StopIteration。返回的值(如果有)用作构造StopIteration的参数,并成为StopIteration.value属性。

在异步生成器函数中,空的return语句表示异步生成器已完成,并且将引发StopAsyncIteration。非空return语句是异步生成器函数中的语法错误。

7.7. yield语句

yield_stmt ::=  yield_expression

yield语句在语义上等效于yield expression。 yield 语句可用于Ellipsis等效的 yield expression 语句中否则需要的括号。例如,yield 语句

yield <expr>
yield from <expr>

等价于 yield expression 语句

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

Yield 表达式和语句仅在定义generator函数时使用,并且仅在生成器函数的主体中使用。在函数定义中使用 yield 足以使该定义创建一个生成器函数,而不是普通函数。

有关yield语义的完整详细信息,请参见Yield expressions部分。

7.8. raise语句

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

如果不存在任何表达式,则raise重新引发当前作用域中活动的最后一个异常。如果当前范围内没有异常处于活动状态,则会引发RuntimeError异常,表明这是一个错误。

否则,raise将第一个表达式评估为异常对象。它必须是BaseException的子类或实例。如果它是一个类,则需要时pass实例化不带参数的类来获取异常实例。

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

通常会在引发异常并将其作为可写的__traceback__属性附加到跟踪对象时自动创建该对象。您可以使用with_traceback()异常方法(返回相同的异常实例,并将其回溯设置为其参数)在一个步骤中创建一个异常并设置自己的回溯,如下所示:

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

from子句用于异常链接:如果给定,则第二个* expression *必须是另一个异常类或实例,然后它将作为__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子句中引发了异常,则类似的机制会隐式工作:然后,将先前的异常附加为新异常的__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

pass在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

有关异常的其他信息,请参见Exceptions部分,有关处理异常的信息,请参见try 语句部分。

在版本 3.3 中进行了更改:raise X from Y中现在允许None作为Y

版本 3.3 中的新增Function:__suppress_context__属性可禁止自动显示异常上下文。

7.9. break语句

break_stmt ::=  "break"

break只能在语法上嵌套在forwhile循环中,而不能嵌套在该循环内的函数或类定义中。

它终止最近的封闭循环,如果循环中有一个,则跳过可选的else子句。

如果for循环被break终止,则循环控制目标保留其当前值。

breakpassfinally子句将控制权从try语句传递出去时,该finally子句在 true 退出循环之前被执行。

7.10. continue 语句

continue_stmt ::=  "continue"

continue只能在语法上嵌套在forwhile循环中,而不能嵌套在该循环内的函数或类定义中。它从最近的封闭循环的下一个循环 continue。

continuepassfinally子句将控制权从try语句中移出时,该finally子句在 true 开始下一个循环之前执行。

7.11. import 语句

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 | "."+

基本导入语句(无from子句)分两个步骤执行:

  • 找到一个模块,必要时加载并初始化

  • 在发生import语句的作用域的本地名称空间中定义一个或多个名称。

当语句包含多个子句(用逗号分隔)时,对每个子句分别执行两个步骤,就好像这些子句已被分隔成单独的 import 语句一样。

import system部分中更详细地介绍了第一步,查找和加载模块的详细信息,该部分还描述了可导入的各种类型的软件包和模块,以及可用于自定义的所有钩子。导入系统。请注意,此步骤中的失败可能表示找不到模块,或者*初始化模块时发生了错误,包括执行模块的代码。

如果成功检索到请求的模块,它将以以下三种方式之一在本地名称空间中可用:

  • 如果模块名称后跟as,则as之后的名称将直接绑定到导入的模块。

  • 如果未指定其他名称,并且正在导入的模块是顶级模块,则该模块的名称将绑定在本地名称空间中,作为对导入模块的引用

  • 如果要导入的模块不是顶级模块,则包含该模块的顶级包的名称绑定在本地名称空间中,作为对顶级包的引用。导入的模块必须使用其完整限定名称而不是直接访问

from表单使用的过程稍微复杂一些:

  • 找到from子句中指定的模块,并在必要时进行加载和初始化;

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

  • 检查导入的模块是否具有该名称的属性

    • 如果不是,请try导入具有该名称的子模块,然后再次检查导入的模块中的该属性

    • 如果找不到该属性,则引发ImportError

    • 否则,对该值的引用存储在本地名称空间中,使用as子句中的名称(如果存在),否则使用属性名称

Examples:

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语句的作用域的本地名称空间中绑定。

模块定义的“公共名称”是pass检查模块的命名空间中名为__all__的变量来确定的;如果已定义,则必须是由该模块定义或导入的名称的字符串序列。 __all__中给出的名称均被视为公开名称,并且必须存在。如果未定义__all__,则公共名称集将包含在模块命名空间中找到的所有名称,这些名称都不以下划线字符('_')开头。 __all__应该包含整个公共 API。目的是避免意外导出不属于 API 的项目(例如,在模块内导入和使用的库模块)。

只能在模块级别导入通配符形式from module import *。try在类或函数定义中使用它会引发SyntaxError

指定要导入的模块时,不必指定模块的绝对名称。当一个模块或程序包包含在另一个程序包中时,可以在同一顶部程序包中进行相对导入,而不必提及程序包名称。pass在from之后的指定模块或包中使用前导点,您可以指定遍历当前包层次结构的高度,而无需指定确切的名称。一个前导点表示进行导入的模块所在的当前包。两点表示一个包装级别。三个点位于两个级别上,依此类推。因此,如果从pkg包中的模块执行from . import mod,则finally将导入pkg.mod。如果从pkg.subpkg1内部执行from ..subpkg2 import mod,则将导入pkg.subpkg2.mod。相对导入的规范包含在包装相对 import部分中。

提供importlib.import_module()以支持动态确定要加载的模块的应用程序。

用参数modulefilenamesys.pathsys.meta_pathsys.path_hooks引发auditing event import

7.11.1. FutureStatements

  • future 语句*是向编译器发出的指令,即应使用语法或语义对特定模块进行编译,这些特定语法将在标准的将来的指定 Python 版本中可用。

将来的语句旨在简化向 Python 的 Future 版本的移植,从而对语言进行不兼容的更改。它允许在该Function成为标准发布之前,按模块使用新Function。

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

将来的语句必须出现在模块顶部附近。在 Future 的语句之前只能出现的几行是:

  • 模块文档字符串(如果有),

  • comments,

  • 空行,和

  • 其他将来的 Statements。

Python 3.7 中唯一需要使用 future 语句的Function是annotations

Python 3 仍然可以识别 future 语句启用的所有历史Function。该列表包括absolute_importdivisiongeneratorsgenerator_stopunicode_literalsprint_functionnested_scopeswith_statement。它们都是冗余的,因为它们始终处于启用状态,并且仅为了向后兼容而保留。

将来的语句在编译时会得到特别的识别和处理:更改核心结构的语义通常是pass生成不同的代码来实现的。甚至可能是新Function引入了新的不兼容语法(例如新的保留字)的情况,在这种情况下,编译器可能需要以不同的方式解析模块。直到运行时才能推迟此类决策。

对于任何给定的发行版,编译器都知道已定义了哪些Function名称,如果将来的语句中包含其不知道的Function,则会引发编译时错误。

直接运行时语义与任何 import 语句相同:有一个标准模块future(稍后描述),它将在执行 future 语句时以通常的方式导入。

有趣的运行时语义取决于 future 语句启用的特定Function。

请注意,该语句没有什么特别之处:

import __future__ [as name]

那不是将来的语句;这是一个普通的 import 语句,没有特殊的语义或语法限制。

默认情况下,在包含将来语句的模块M中,pass调用内置函数exec()compile()编译的代码将默认使用与该将来语句关联的新语法或语义。可以passcompile()的可选参数来控制它-有关详细信息,请参见该函数的文档。

在交互式解释器提示符下键入的将来语句将在其余解释器会话中生效。如果解释器以-i选项启动,并传递了要执行的脚本名称,并且该脚本包含将来的语句,则它将在执行脚本后启动的交互式会话中生效。

See also

  • PEP 236-返回_Future__

  • __future_机制的原始建议。

7.12. Global 语句

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

global语句是一个语句,适用于整个当前代码块。这意味着列出的标识符将被解释为全局变量。如果没有global,则不可能分配给全局变量,尽管自由变量可能引用全局变量而未语句为全局变量。

global语句中列出的名称不得在文本上在该global语句之前的同一代码块中使用。

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

CPython 实现细节: 当前实现未强制执行其中一些限制,但是程序不应滥用此自由,因为将来的实现可能会强制执行这些限制或默默更改程序的含义。

程序员注: global是解析器的指令。它仅适用于与global语句同时解析的代码。特别地,包含在提供给内置exec()函数的字符串或代码对象中的global语句不会影响包含该函数调用的代码块,并且包含在该字符串中的代码不受包含在其中的代码中global语句的影响函数调用。 eval()compile()函数也是如此。

7.13. nonlocal 语句

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

nonlocal语句使列出的标识符引用最近的包围范围中的先前绑定的变量(全局变量除外)。这很重要,因为绑定的默认行为是首先搜索本地名称空间。该语句允许封装的代码在全局(模块)范围之外的本地范围之外重新绑定变量。

与在global语句中列出的名称不同,在nonlocal语句中列出的名称必须引用封闭范围内的现有绑定(不能明确确定应在其中创建新绑定的范围)。

nonlocal语句中列出的名称不得与本地范围内的现有绑定冲突。

See also