Skip to main content

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
                 | 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 的语法定义,请参见 主要 部分。)

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

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

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

  • 如果目标列表是空的:对象也必须是一个空的可迭代。

  • 如果目标列表是括号中的单个目标:对象分配给该目标。

  • 如果目标列表是以逗号分隔的目标列表,或者在方括号中的单个目标:对象必须是具有与目标列表中的目标相同数量的项目的迭代,并且项目被分配,从左向右,到相应的目标。

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

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

对象到单个目标的分配被递归地定义如下。

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

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

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

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

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

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

    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__() 方法。

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

在当前实现中,目标的语法被视为与表达式的语法相同,并且在代码生成阶段期间拒绝无效语法,导致较少详细的错误消息。

虽然赋值的定义意味着左侧和右侧之间的重叠是“同时的”(例如 a, b = b, a 交换两个变量),重叠 within,分配给变量的集合从左到右发生,有时导致混乱。例如,以下程序打印 [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 行为之外,由扩充赋值执行的二进制操作与正常二进制操作相同。

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

7.2.2. 带注释的赋值语句

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

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

与正常 分配语句 的区别在于,仅允许单个目标值且仅允许单个右侧值。

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

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

如果在函数作用域中注释了名称,则该名称对于该作用域是局部的。注释绝不会被求值并存储在函数范围中。

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

参见

PEP 526 - 变量和属性注释语法 PEP 484 - 类型提示

7.3. assert 声明

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 expression语句中需要的括号。例如,yield语句

yield <expr>
yield from <expr>

等价于yield expression语句

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

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

有关 yield 语义的完整详细信息,请参阅 产量表达式 部分。

7.8. raise 声明

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

如果没有表达式,raise 重新提出当前范围中活动的最后一个异常。如果当前范围中没有活动的异常,则会引发 RuntimeError 异常,指示这是错误。

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

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

traceback对象通常在引发异常时自动创建,并作为可写的 __traceback__ 属性附加到它。您可以使用 with_traceback() 异常方法(返回相同的异常实例,将其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: int division or modulo 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: int division or modulo 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

关于异常的附加信息可以在 例外 部分找到,关于处理异常的信息在 try 声明 部分。

7.9. break 声明

break_stmt ::=  "break"

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

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

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

break 通过带有 finally 子句的 try 语句传递控制时,该 finally 子句在真正离开循环之前执行。

7.10. continue 声明

continue_stmt ::=  "continue"

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

continue 通过带有 finally 子句的 try 语句传递控制时,在真正开始下一个循环之前执行该 finally 子句。

7.11. import 声明

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

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

  1. 找到一个模块,如果需要,加载和初始化它

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

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

第一步,查找和加载模块的细节在 进口系统 一节中有更详细的描述,它还描述了可以导入的各种类型的包和模块,以及可用于自定义的所有钩子进口系统。请注意,此步骤中的故障可能表示无法找到模块,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 之后使用指定模块或包中的前导点,您可以指定在不指定确切名称的情况下遍历当前包层次结构的高度。一个前导点表示存在导入模块的当前包。两个点意味着一个包级别。三个点是两个级别等等。所以如果你从 pkg 包中的模块执行 from . import mod,那么你将最终导入 pkg.mod。如果从 pkg.subpkg1 中执行 from ..subpkg2 import mod,您将导入 pkg.subpkg2.mod。相对导入的规范包含在 PEP 328 中。

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

7.11.1. 未来陈述

future statement 是对编译器的指示,即特定模块应该使用语法或语义来编译,该语法或语义将在特定的未来版本的Python中可用,其中该特征成为标准。

未来的语句旨在简化向未来版本的Python的迁移,从而对语言引入不兼容的更改。它允许在特性成为标准的版本之前在每个模块的基础上使用新功能。

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

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

  • 模块docstring(如果有),

  • 注释,

  • 空白行和

  • 其他未来陈述。

Python 3.0识别的功能是 absolute_importdivisiongeneratorsunicode_literalsprint_functionnested_scopeswith_statement。它们都是冗余的,因为它们总是启用的,并且只保留用于向后兼容性。

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

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

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

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

注意,没有什么特别的语句:

import __future__ [as name]

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

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

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

参见

PEP 236 - 返回__future__

原来关于__future__机制的提案。

7.12. global 声明

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

global 语句是对整个当前代码块保持的声明。这意味着列出的标识符将被解释为全局变量。这将是不可能分配到没有 global 的全局变量,虽然自由变量可能引用全局变量而没有声明全局。

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

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

当前的实现不强制执行这些限制,但是程序不应该滥用这种自由,因为未来的实现可以强制它们或者静默地改变程序的含义。

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

7.13. nonlocal 声明

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

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

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

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

参见

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

nonlocal 语句的规范。