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

(见章节) 初选 对于的语法定义 属性因子订阅切片

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

根据目标(列表)的形式递归定义赋值。当目标是可变对象(属性引用、订阅或切片)的一部分时,可变对象必须最终执行赋值并决定其有效性,如果赋值不可接受,则可能引发异常。各种类型观察到的规则和引发的异常随对象类型的定义而给出(请参见第节 标准类型层次结构

对象到目标列表的赋值,可以选择括在括号或方括号中,递归定义如下。

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

  • 否则:对象必须是一个iterable,其项数必须与目标列表中的目标数相同,并且从左到右将这些项分配给相应的目标。

    • 如果目标列表包含一个带星号前缀的目标,称为“带星号”目标:该对象必须是一个可iterable,其项至少与目标列表中的目标数相等,减去1。从左到右,将iterable的第一个项分配给星标目标之前的目标。iterable的最后一个项被分配给目标,在目标后加上星号。然后将iterable中剩余项目的列表分配给带星标的目标(列表可以为空)。

    • 否则:对象必须是一个iterable,其项数必须与目标列表中的目标数相同,并且从左到右将这些项分配给相应的目标。

将一个对象分配给一个目标是递归定义的,如下所示。

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

    • 如果名称不出现在 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 只计算一次。此外,在可能的情况下,执行实际操作 in-place 也就是说,不创建新对象并将其分配给目标,而是修改旧对象。

与常规分配不同,增强分配评估左侧 before 评估右侧。例如, a[i] += f(x) 先抬头看 a[i] ,然后评估 f(x) 执行加法,最后将结果写回 a[i] .

除了在一条语句中分配给元组和多个目标之外,扩充的赋值语句完成的赋值处理方式与普通赋值相同。同样,除了可能的 in-place 行为,通过增广赋值执行的二进制操作与普通的二进制操作相同。

对于属性引用的目标,相同 caveat about class and instance attributes 适用于常规任务。

7.2.2. 带批注的工作分配语句

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

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

与正常的区别 工作分配声明 是否只允许单个目标。

对于作为分配目标的简单名称,如果在类或模块范围内,注释将被计算并存储在一个特殊的类或模块属性中。 __annotations__ 这是一个从变量名(如果是私有的,则会损坏)到评估注释的字典映射。该属性是可写的,如果静态地找到注释,则会在类或模块体执行开始时自动创建该属性。

对于作为赋值目标的表达式,如果在类或模块范围内,则计算注释,但不存储注释。

如果在函数作用域中注释了名称,则该名称是该作用域的本地名称。注释从不在函数作用域中计算和存储。

如果存在右手边,则在评估注释(如果适用)之前,注释赋值将执行实际赋值。如果表达式目标的右侧不存在,则解释器将对目标进行除最后一个以外的计算 __setitem__()__setattr__() 调用。

参见

PEP 526 -变量注释的语法

该建议增加了注释变量类型(包括类变量和实例变量)的语法,而不是通过注释来表达它们。

PEP 484 类型提示

添加了 typing 模块为可用于静态分析工具和IDE的类型注释提供标准语法。

在 3.8 版更改: 现在,带注释的赋值允许在右侧使用与常规赋值相同的表达式。以前,一些表达式(如未加括号的元组表达式)导致了语法错误。

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)

这些等价物假设 __debug__AssertionError 引用具有这些名称的内置变量。在当前的实现中,内置变量 __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 )作为返回值。

什么时候? return 将控制权从 try 带有 finally 条款,即 finally 子句在真正离开函数之前执行。

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

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

7.7. 这个 yield 陈述

yield_stmt ::=  yield_expression

A yield 语句在语义上等价于 yield expression . yield语句可用于省略等效yield expression语句中所需的括号。例如,yield语句:

yield <expr>
yield from <expr>

等价于yield表达式语句:

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

只有在定义 generator 函数,并且仅用于生成器函数体中。在函数定义中使用yield足以使该定义创建生成器函数,而不是普通函数。

有关 yield 语义,参考 返回表达式 部分。

7.8. 这个 raise 陈述

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

如果不存在表达式, raise 重新引发当前作用域中最后一个活动的异常。如果当前作用域中没有活动的异常,则 RuntimeError 引发异常,指示这是一个错误。

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

这个 type 异常是异常实例的类, value 是实例本身。

当异常被引发并作为 __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 子句:上一个异常随后作为新异常附加 __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

通过指定 Nonefrom 条款::

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

有关异常的其他信息,请参见第节。 例外情况 ,有关处理异常的信息在第节中 这个 try 陈述 .

在 3.3 版更改: None 现在允许作为 Y 在里面 raise X from Y .

3.3 新版功能: 这个 __suppress_context__ 属性以禁止自动显示异常上下文。

7.9. 这个 break 陈述

break_stmt ::=  "break"

break 只能出现在 forwhile 循环,但不嵌套在该循环中的函数或类定义中。

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

如果A for 循环终止于 break 循环控制目标保持其当前值。

什么时候? break 将控制权从 try 带有 finally 条款,即 finally 在真正离开循环之前执行子句。

7.10. 这个 continue 陈述

continue_stmt ::=  "continue"

continue 只能出现在 forwhile 循环,但不嵌套在该循环中的函数或类定义中。它将继续进行最近的封闭循环的下一个循环。

什么时候? continue 将控制权从 try 带有 finally 条款,即 finally 在真正开始下一个循环周期之前执行子句。

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 子句)分两步执行:

  1. 查找模块,必要时加载并初始化

  2. 在本地命名空间中为 import 出现语句。

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

第一步、查找和加载模块的详细信息在 import system 它还描述了可以导入的各种类型的包和模块,以及可用于自定义导入系统的所有挂钩。注意,此步骤中的故障可能表明模块无法定位, or 初始化模块时出错,其中包括模块代码的执行。

如果成功地检索到请求的模块,则可以通过以下三种方式之一在本地命名空间中使用该模块:

  • 如果模块名称后跟 as ,然后是下面的名称 as 直接绑定到导入的模块。

  • 如果没有指定其他名称,并且要导入的模块是顶级模块,则该模块的名称将作为对导入模块的引用绑定到本地命名空间中。

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

这个 from 表单使用稍微复杂一点的过程:

  1. 查找中指定的模块 from 条款,必要时加载并初始化;

  2. 对于中指定的每个标识符 import 条款:

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

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

    3. 如果找不到属性, ImportError 提高了。

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

实例:

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 .

指定要导入的模块时,不必指定模块的绝对名称。当一个模块或包包含在另一个包中时,可以在同一个顶级包中进行相对导入,而无需提及包名称。在指定的模块或包中使用前导点 from 您可以指定沿当前包层次结构向上遍历的高度,而不指定确切的名称。一个前导点表示当前存在导入模块的包。两个点代表一个包裹级别。三个点在上两层,等等,所以如果你执行 from . import mod 从模块中 pkg 然后您将最终导入 pkg.mod . 如果执行 from ..subpkg2 import mod 从内 pkg.subpkg1 你将进口 pkg.subpkg2.mod . 相关导入的规范包含在 包相对导入 部分。

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

提出一个 auditing event import 带着论据 modulefilenamesys.pathsys.meta_pathsys.path_hooks .

7.11.1. 未来报表

A future statement 是对编译器的一个指令,指示应使用语法或语义编译特定模块,该语法或语义将在将来指定的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

未来的语句必须出现在模块顶部附近。在将来的语句之前只能出现以下行:

  • 模块docstring(如果有),

  • 评论,

  • 空行,以及

  • 其他未来报表。

唯一需要使用未来语句的功能是 annotations (请参阅 PEP 563 )。

由future语句启用的所有历史特性仍然可以被python 3识别。清单包括 absolute_importdivisiongeneratorsgenerator_stopunicode_literalsprint_functionnested_scopeswith_statement . 它们都是冗余的,因为它们总是被启用的,并且只保持向后兼容性。

未来的语句在编译时被识别和处理:对核心构造语义的更改通常通过生成不同的代码来实现。甚至可能是新特性引入了新的不兼容语法(例如新的保留字),在这种情况下,编译器可能需要以不同的方式分析模块。在运行时之前,不能推迟这些决定。

对于任何给定的版本,编译器都知道已经定义了哪些特性名称,并且如果未来的语句包含它不知道的特性,则会引发编译时错误。

直接运行时语义与任何导入语句相同:有一个标准模块 __future__ ,稍后描述,它将在执行未来语句时以常规方式导入。

有趣的运行时语义取决于由未来语句启用的特定功能。

注意,这句话没有什么特别的地方:

import __future__ [as name]

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

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

在交互式解释程序提示下键入的未来语句将在解释程序会话的其余部分生效。如果解释程序是用 -i 选项,将一个脚本名传递给要执行的脚本,并且该脚本包含一个将来的语句,它将在脚本执行后启动的交互式会话中生效。

参见

PEP 236 -回到 __future__

针对 __future__ 机制的原始建议

7.12. 这个 global 陈述

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

这个 global 语句是为整个当前代码块保留的声明。这意味着列出的标识符将被解释为全局标识符。如果没有 global 尽管自由变量可以引用全局变量而不声明为全局变量。

A中列出的名称 global 语句不能在该语句前面的同一代码块中使用 global 语句。

列表中列出的名称 global 语句不能定义为形参,也不能定义为中的目标 with 声明或 except 子句,或在 for 目标列表, class 定义、函数定义 import 语句或变量批注。

CPython implementation detail: 当前的实现并不强制执行其中的一些限制,但是程序不应该滥用这种自由,因为将来的实现可能强制执行这些限制,或者悄悄地改变程序的含义。

编程器说明: global 是对分析器的指令。它仅适用于与 global 语句。尤其是, global 包含在提供给内置函数的字符串或代码对象中的语句 exec() 函数不影响代码块 包含 函数调用和包含在此类字符串中的代码不受 global 包含函数调用的代码中的语句。同样适用于 eval()compile() 功能。

7.13. 这个 nonlocal 陈述

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

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

A中列出的名称 nonlocal 语句,不同于 global 语句必须引用封闭范围中预先存在的绑定(不能明确确定应在其中创建新绑定的范围)。

A中列出的名称 nonlocal 语句不能与本地作用域中预先存在的绑定冲突。

参见

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

规范 nonlocal 语句。