7. 简单语句¶
简单语句由一个逻辑行构成。在由分号分隔的单个行上可能发生几个简单的语句。简单语句的语法是:
simple_stmt ::=expression_stmt
|assert_stmt
|assignment_stmt
|augmented_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
(有关attributeref,subscription和slicing的语法定义,请参见原语一节。)
赋值语句评估表达式列表(请记住,这可以是单个表达式或逗号分隔的列表,后者产生一个元组),并将单个结果对象从左到右分配给每个目标列表。
根据目标(列表)的形式递归定义分配。当目标是可变对象(属性引用,订阅或切片)的一部分时,可变对象必须最终执行分配并决定其有效性,并且如果分配是不可接受的,则可能引发异常。各种类型遵守的规则以及抛出的异常根据对象类型的定义给出(参见标准类型的层次结构一节)。
将对象分配给目标列表,可以括在圆括号或方括号中,递归地定义如下。
- 如果目标列表为空:对象也必须是一个空的可迭代对象。
- 如果目标列表位于括号中的单个目标:将对象分配给该目标。
- 如果目标列表是以逗号分隔的目标列表,或者在方括号中的单个目标:对象必须是具有与目标列表中的目标相同数量的项目的一个可迭代对象,并且项目被从左向右赋值给相应的目标。
- 如果目标列表包含一个带有星号(前缀为星号,称为“已加星标”的目标)的目标:对象必须是比目标列表中的目标项目数少一个的可迭代对象。可迭代对象的第一项从左到右赋值到已加星标的目标之前的目标。可迭代对象的最终项目分配给加星标的目标后的目标。然后将可迭代对象中剩余项目的列表分配给加星标的目标(列表可以为空)。
- 否则:对象必须是与目标列表中的目标具有相同项目数量的可迭代对象,并且项目从左到右赋值给相应的目标。
对象到单个目标的分配递归定义如下。
如果目标是标识符(名称):
如果名称已绑定,则名称将反弹。这可能导致先前绑定到名称的对象的引用计数达到零,导致对象被释放,并且它的析构函数(如果它有一个)被调用。
如果目标是属性引用:引用中的主表达式是计算。它产生的对象应该具有一个可以赋值的属性;如果情况不是这样,则抛出
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()
创建的属性。如果目标是订阅:引用中的主表达式是计算。它应该产生一个可变序列对象(如列表)或映射对象(如字典)。接下来,下标表达式是计算。
如果primary是一个可变的序列对象(例如一个列表),则下标必须产生一个普通的整数。如果为负,则将序列的长度添加到其中。结果值必须是小于序列长度的非负整数,并且要求序列将分配的对象分配给具有该索引的项目。如果索引超出范围,则引发
IndexError
异常(给序列下标赋值不能添加新的元素到序列中)。如果主体是映射对象(例如字典),则下标必须具有与映射的键类型兼容的类型,然后要求映射创建键/基准对,将下标映射到分配的对象。这可以用相同的键值替换现有键/值对,或插入新的键/值对(如果没有具有相同值的键)。
对于用户定义的对象,使用适当的参数调用
__ setitem __()
方法。如果目标是切片:引用中的主表达式是计算。它应该产生一个可变的序列对象(如列表)。分配的对象应该是相同类型的序列对象。下一步,如果存在,则计算下边界和上边界表达式;默认是零和序列的长度。边界计算的值应该是整数。如果bound是负数,则将序列的长度添加到它。所得到的边界被裁剪以位于零和序列的长度之间,包括端点。最后,要求序列对象用分配的序列的项替换片。切片的长度可能不同于赋值的序列的长度,因此如果对象允许则改变目标序列的长度。
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)
也可以看看
- 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]
。
除了在单个语句中分配元组和多个目标之外,由扩充赋值语句完成的赋值操作与正常赋值操作相同。类似地,除了可能的in-place行为,由扩充赋值执行的二进制操作与常规二进制操作相同。
如果target是属性引用,关于类和实例属性的注意事项同样适用于正常的赋值。
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
,在要求优化时(命令行选项-O)为False
。当编译时请求优化时,当前代码生成器不发出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
在语法上只可以出现在函数定义中,不可以出现在类定义中。
如果存在expression_list,则计算它,否则使用None
替换。
return
离开当前的函数调用时以expression_list(或None
)作为返回值。
当return
将控制传出带有finally
子句的try
语句时,在真正离开函数之前会执行finally
子句。
在生成器函数中,return
语句表示生成器已经完成并将引发StopIteration
。返回值(如果有)用作构造StopIteration
的参数,并成为StopIteration.value
属性。
7.7. yield
语句¶
yield_stmt ::= yield_expression
yield
语句在语义上等同于yield表达式。yield语句可以用来省略在等效的yield expression语句中需要的括号。例如,yield语句
yield <expr>
yield from <expr>
等价于yield expression语句
(yield <expr>)
(yield from <expr>)
yield语句只在定义生成器函数时使用,且只在生成器函数的函数体中使用。在函数定义中使用yield语句就足以创建一个生成器函数而不是普通函数。
7.8. raise
语句¶
raise_stmt ::= "raise" [expression
["from"expression
]]
如果没有表达式,raise
重新抛出当前作用域中最后一个激活的异常。如果当前作用域中没有活动的异常,则抛出TypeError
异常以表示这是一个错误。
否则,raise
会将第一个表达式计算为异常对象。它必须是BaseException
的子类或实例。如果它是一个类,当需要时,将通过实例化没有参数的类来获得异常实例。
异常的类型是异常实例的类,值是实例本身。
通常在引发异常时将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
7.9. break
语句¶
break_stmt ::= "break"
break
只能在语法上嵌套在for
或while
循环中,但不能嵌套在该循环中的函数或类定义中。
它终止最近的外围循环,如果循环有一个可选的else
子句,则跳过它。
7.10. continue
语句¶
continue_stmt ::= "continue"
continue
在语法上只可以出现在for
或while
循环中,但不能嵌套在这些循环内的函数定义、类定义和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
子句)分两步执行:
- 找到一个模块,如果需要,加载和初始化它
- 定义
import
语句所在作用域的局部命名空间中的一个或多个名称。
当语句包含多个子句(以逗号分隔)时,为每个子句分别执行这两个步骤,就像子句已被分隔为单独的import语句一样。
第一步,查找和加载模块的细节在导入系统部分有更详细的描述,它还描述了可以导入的各种类型的包和模块,以及所有可用于定制导入系统的钩子。请注意,此步骤中的失败可能表示模块无法找到或表示在初始化模块时发生错误,包括执行模块代码。
如果请求的模块被成功检索,它将通过以下三种方式之一在本地命名空间中可用:
- 如果模块名称后面是
as
,则as
之后的名称将直接绑定到导入的模块。 - 如果未指定其他名称,并且正在导入的模块是顶级模块,则模块的名称将在本地命名空间中绑定为对导入模块的引用
- 如果正在导入的模块不是顶级模块,则包含该模块的顶级包的名称在本地命名空间中被绑定为对顶级包的引用。导入的模块必须使用其完全限定名称而不是直接访问
from
形式使用稍微更复杂的过程:
- 找到
from
子句中指定的模块,如果需要,加载和初始化它; - 对于
import
子句中指定的每个标识符:- 检查导入的模块是否具有该名称的属性
- 如果没有,请尝试导入具有该名称的子模块,然后再次检查导入的模块的该属性
- 如果未找到该属性,则引发
ImportError
。 - 否则,对该值的引用存储在本地命名空间中,使用
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语句¶
future语句是一个针对编译器的指令,它指示某个特定的模块应该使用在未来版本的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_import
,division
,generators
,unicode_literals
,print_function
,nested_scopes
和with_statement
。它们都是冗余的,因为它们始终启用,并且只保留用于向后兼容性。
未来语句在编译时被识别和特别处理:核心构造的语义的改变通常通过生成不同的代码来实现。甚至可能出现新特性引入新的不兼容语法(例如新的保留字)的情况,在这种情况下,编译器可能需要不同地解析模块。这样的决定不能推迟到运行时。
对于任何给定的版本,编译器知道已经定义了哪些特征名称,并且如果将来的语句包含其未知的特征,则引发编译时错误。
直接运行时语义与任何导入语句相同:有一个标准模块__future__
,它将在执行未来语句时以通常的方式导入,稍后描述。
有趣的运行时语义取决于未来语句所启用的特定功能。
注意这个语句没有什么特别的:
import __future__ [as name]
它不是future 语句;它是一个普通的没有特殊语义和语法限制的import 语句。
在包含future语句的模块M
中,调用内建函数exec()
和compile()
编译的代码默认将使用与future语句关联的新的语法或语义。这种行为可以通过compile()
可选的参数可以控制 — 详细信息参见该函数的文档。
在交互式解释器提示符处键入的未来语句将对解释器会话的其余部分生效。如果解释器以-i
选项启动,然后传入一个脚本的名称给它执行,且脚本包含一个future 语句,它将在脚本执行之后的交互式会话中生效。
也可以看看
- PEP 236 —— 回到__future__
- 原来关于__future__机制的提案。
7.12. global
语句¶
global_stmt ::= "global"identifier
(","identifier
)*
global
语句是针对当前整个代码块的声明。这意味着列出的标识符将被解释为全局变量。不用global
给全局变量赋值是不可能实现的,尽管自由变量可以引用全局变量而不用声明为全局的。
在相同的代码块中,global
语句中列出的名称不可以在global
语句之前使用。
global
语句列出的名称不可定义为形式参数或者定义在for
循环的控制目标、类
定义、函数定义或import
语句中。
CPython实现细节: 目前的实现没有强制实行后两个限制,但是程序不应该滥用这个自由,因为未来的实现可能强制实行或者默默地改变程序的含义。
程序员注意:global
是针对解析器的指令。它只应用于与global
语句同一时刻解析的代码。特别地,内建函数exec()
中包含的global
语句不会影响包含这个函数调用的代码块,包含这个函数调用的代码中的global
语句不影响这个字符串中包含的代码。这同样适用于eval()
和compile()
函数。
7.13. nonlocal
语句¶
nonlocal_stmt ::= "nonlocal"identifier
(","identifier
)*
nonlocal
语句使得列出的标识符引用之前在最近的外围作用域中绑定的变量,全局变量除外。这很重要,因为绑定的默认行为是首先搜索本地命名空间。该语句允许封装的代码重新绑定局部作用域外面的变量,除全局(模块)作用域之外。
与global
语句中列出的名称不同,nonlocal
语句中列出的名称必须引用外围作用域中的预先存在的绑定(创建新绑定的作用域不能明确地确定)。
在nonlocal
语句中列出的名称不得与局部作用域中的预先存在的绑定冲突。