7. 简单语句 — Python 文档

来自菜鸟教程
Python/docs/3.9/reference/simple stmts
跳转至:导航、​搜索

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

(有关 attributerefsubscriptionslicing 的语法定义,请参阅 Primaries 部分。)

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

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

将对象分配给目标列表,可选择括在圆括号或方括号中,递归定义如下。

  • 如果目标列表是没有尾随逗号的单个目标(可选在括号中),则将对象分配给该目标。
  • Else:对象必须是与目标列表中有相同数量项目的可迭代对象,并且项目从左到右分配给相应的目标。
    • 如果目标列表包含一个以星号为前缀的目标,称为“带星号”的目标:该对象必须是一个可迭代对象,其项数至少与目标列表中的目标数一样多,减去一个。 可迭代对象的第一项从左到右分配给加星标目标之前的目标。 可迭代对象的最后一项被分配给加星标的目标之后的目标。 然后将迭代中剩余项目的列表分配给加星标的目标(该列表可以为空)。
    • Else:对象必须是与目标列表中有相同数量项目的可迭代对象,并且项目从左到右分配给相应的目标。

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

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

    • 如果名称未出现在当前代码块中的 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__() 方法。

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

尽管赋值的定义意味着左侧和右侧之间的重叠是“同时的”(例如 a, b = b, a 交换两个变量),但在 中重叠了 分配的集合-to 变量从左到右出现,有时会导致混淆。 例如,以下程序打印 [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                     ::=  "+=" | "-=" | "*=" | "@=" | "/=" | "//=" | "%=" | "**="
                               | ">>=" | "<<=" | "&=" | "^=" | "|="

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

扩充赋值计算目标(与普通赋值语句不同,它不能是解包语句)和表达式列表,对两个操作数执行特定于赋值类型的二元运算,并将结果分配给原始目标。 目标仅评估一次。

x += 1 这样的增强赋值表达式可以重写为 x = x + 1 以实现类似但不完全相同的效果。 在增强版本中,x 只计算一次。 此外,如果可能,实际操作会在 就地 执行,这意味着不是创建新对象并将其分配给目标,而是修改旧对象。

与正常分配不同,增强分配先评估左侧 ,然后 评估右侧。 例如,a[i] += f(x)首先查找a[i],然后计算f(x)并进行加法,最后将结果写回a[i]

除了在单个语句中为元组和多个目标赋值之外,由扩充赋值语句完成的赋值与普通赋值的处理方式相同。 类似地,除了可能的 就地 行为之外,由增广赋值执行的二元运算与正常的二元运算相同。

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


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 将控制权从带有 finally 子句的 try 语句传递出去时,该 finally 子句在真正离开函数之前被执行。

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

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


7.7. 这yield陈述

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. 这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__ 属性(可写)附加到引发的异常。 如果表达式是异常类,则该类将被实例化,并且生成的异常实例将作为 __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

通过在 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 中的 Y


3.3 版新功能: __suppress_context__ 属性抑制异常上下文的自动显示。


7.9. 这break陈述

break_stmt ::=  "break"

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

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

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

break 将控制权从带有 finally 子句的 try 语句传递出去时,该 finally 子句在真正离开循环之前被执行。


7.10. 这continue陈述

continue_stmt ::=  "continue"

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

continue 将控制权从带有 finally 子句的 try 语句传递出去时,该 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" relative_module "import" "*"
module          ::=  (identifier ".")* identifier
relative_module ::=  "."* module | "."+

基本的导入语句(没有 from 子句)分两步执行:

  1. 找到一个模块,必要时加载并初始化它
  2. 在本地命名空间中为发生 import 语句的范围定义一个或多个名称。

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

第一步,查找和加载模块的细节在导入系统部分有更详细的描述,该部分还描述了可以导入的各种类型的包和模块,以及所有的可用于自定义导入系统的钩子。 请注意,此步骤中的失败可能表明模块无法定位, 初始化模块时发生错误,其中包括模块代码的执行。

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

  • 如果模块名后面跟着as,那么as后面的名字直接绑定到导入的模块上。
  • 如果没有指定其他名称,并且被导入的模块是顶级模块,则该模块的名称绑定在本地命名空间中作为对导入模块的引用
  • 如果正在导入的模块是 不是 顶级模块,则包含该模块的顶级包的名称绑定在本地命名空间中作为对顶级包的引用。 必须使用其全限定名而不是直接访问导入的模块

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

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

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

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

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

7.11.1. 未来的陈述

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

future 语句必须出现在模块顶部附近。 可以出现在 future 语句之前的唯一行是:

  • 模块文档字符串(如果有),
  • 评论,
  • 空行,和
  • 其他未来声明。

唯一需要使用 future 语句的功能是 annotations(参见 PEP 563)。

所有由 future 语句启用的历史特性仍然被 Python 3 识别。 列表包括 absolute_importdivisiongeneratorsgenerator_stopunicode_literalsprint_functionnested_scopes ] 和 with_statement。 它们都是冗余的,因为它们始终处于启用状态,并且仅用于向后兼容。

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

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

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

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

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

import __future__ [as name]

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

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

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

也可以看看

PEP 236 - 回到__未来__
__future__ 机制的原始提议。


7.12. 这global陈述

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

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

global 语句中列出的名称不得用于该 global 语句之前的同一个代码块中。

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

程序员注: 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 包含 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. 这nonlocal陈述

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

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

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

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

也可以看看

PEP 3104 - 访问外部作用域中的名称
nonlocal 语句的规范。