表达式语句与作业语句的声明(7)Python语言(语法教程)(参考资料)
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
(有关attributeref, subscription和切片的语法定义,请参阅Primaries一节。)
赋值语句计算表达式列表(请记住,这可以是单个表达式或以逗号分隔的列表,后者产生元组)并从左到右将单个结果对象分配给每个目标列表。
分配是根据目标(列表)的形式递归定义的。当目标是可变对象(属性引用,订阅或切片)的一部分时,可变对象必须最终执行赋值并确定其有效性,并且如果赋值是不可接受的,则可能引发异常。各种类型观察到的规则和引发的异常都是通过对象类型的定义给出的(请参阅标准类型层次结构一节)。
将对象分配给目标列表,可选地括在括号或方括号中,递归地定义如下。
- 如果目标列表是没有尾随逗号的单个目标,可选择在括号中,则将该对象分配给该目标。
- 否则:对象必须是具有与目标列表中的目标相同数量的项目的可迭代对象,并且项目从左到右分配给相应的目标。
- 如果目标列表包含一个前缀为星号的目标,称为“已加星标”的目标:该对象必须是一个可迭代的,其项目数至少与目标列表中的目标一样多,减去一个。迭代的第一项从左到右分配给加星标的目标之前的目标。迭代目标后,可迭代的最终项目将分配给目标。然后将迭代中的剩余项目列表分配给已加星标的目标(列表可以为空)。
- 否则:对象必须是具有与目标列表中的目标相同数量的项目的可迭代对象,并且项目从左到右分配给相应的目标。
如下递归地定义对象到单个目标的分配。
-
如果目标是标识符(名称):
如果已经绑定,该名称将被反弹。这可能导致先前绑定到名称的对象的引用计数达到零,从而导致对象被释放并且其析构函数(如果有的话)被调用。
-
如果目标是属性引用:将评估引用中的主表达式。它应该产生一个具有可分配属性的对象; 如果不是这种情况,
TypeError
则提出。然后要求该对象将指定的对象分配给给定的属性; 如果它不能执行赋值,则会引发异常(通常但不一定AttributeError
)。注意:如果对象是类实例,并且属性引用出现在赋值运算符的两侧,则RHS表达式
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()
。 -
如果目标是订阅:将评估引用中的主表达式。它应该产生可变序列对象(例如列表)或映射对象(例如字典)。接下来,评估下标表达式。
如果primary是可变序列对象(例如列表),则下标必须生成整数。如果是负数,则将序列的长度添加到其中。结果值必须是小于序列长度的非负整数,并且要求序列将分配的对象分配给具有该索引的项目。如果索引超出范围,
IndexError
则引发(分配给下标序列不能将新项添加到列表中)。如果primary是映射对象(例如字典),则下标必须具有与映射的键类型兼容的类型,然后要求映射创建将下标映射到指定对象的键/数据对。这可以用相同的键值替换现有的键/值对,或者插入新的键/值对(如果不存在具有相同值的键)。
对于用户定义的对象,
__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)
也可以看看
- PEP 3132 – 扩展的可迭代解包
*target
功能规范。
7.2.1。增强赋值语句
增强赋值是在单个语句中组合二元运算和赋值语句:
augmented_assignment_stmt :: = (| ) augtarget :: = | | | augop :: =“+ =”| “ - =”| “* =”| “@ =”| “/ =”| “// =”| “%=”| “** =” augtarget augopexpression_listyield_expressionidentifierattributerefsubscriptionslicing | “>> =”| “<< =”| “&=”| “^ =”| “| =”
(有关最后三个符号的语法定义,请参阅Primaries一节。)
增强赋值评估目标(与正常赋值语句不同,它不能是解包)和表达式列表,执行特定于两个操作数上的赋值类型的二进制运算,并将结果赋给原始目标。目标仅评估一次。
增强的赋值表达式可以被重写为实现类似但不完全相同的效果。在增强版中,仅评估一次。此外,在可能的情况下,实际操作是就地执行的,这意味着不是创建新对象并将其分配给目标,而是修改旧对象。x += 1
x = x + 1
x
与正常分配不同,增强分配 在评估右侧之前评估左侧。例如,首先查找,然后它评估并执行添加,最后,它将结果写回。a[i] += f(x)
a[i]
f(x)
a[i]
除了在单个语句中分配元组和多个目标之外,由扩充赋值语句完成的赋值的处理方式与正常赋值相同。类似地,除了可能 的就地行为之外,通过扩充分配执行的二进制操作与普通二进制操作相同。
对于属性引用的目标,关于类和实例属性的相同警告适用于常规分配。
7.2.2。带注释的赋值语句
注释赋值是单个语句中变量或属性注释与可选赋值语句的组合:
annotated_assignment_stmt :: = augtarget “:” expression [“=” expression ]
与正常Assignment语句的不同之处在于,只允许单个目标且仅允许单个右侧值。
对于简单名称作为赋值目标,如果在类或模块作用域中,则会对注释进行求值并将其存储在特殊的类或模块属性中__annotations__
,该属性是从变量名称(如果是私有的)损坏到评估的注释的字典映射。如果静态找到注释,则此属性是可写的,并在类或模块正文执行开始时自动创建。
对于作为赋值目标的表达式,如果在类或模块范围内,则会评估注释,但不会存储注释。
如果在函数作用域中注释了名称,则该名称对于该作用域是本地的。永远不会评估注释并将其存储在函数范围中。
如果存在右侧,则带注释的赋值在评估注释之前执行实际赋值(如果适用)。如果表达式目标不存在右侧,则解释器将评估除最后一个__setitem__()
或__setattr__()
调用之外的目标 。
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
是一个空操作 – 当它被执行时,没有任何反应。当语法需要语句但不需要执行任何代码时,它可用作占位符,例如:
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
甲yield
语句是语义上等同于一个产量表达。yield语句可用于省略等效yield表达式语句中必需的括号。例如,yield语句
yield <expr>
yield from <expr>
等价于yield表达式语句
(yield <expr>)
(yield from <expr>)
Yield表达式和语句仅在定义生成器 函数时使用,并且仅在生成器函数的主体中使用。在函数定义中使用yield足以使该定义创建生成器函数而不是正常函数。
有关yield
语义的完整详细信息,请参阅 Yield表达式部分。
7.8。该raise
声明
raise_stmt :: =“ up ”[ expression[“from” expression]]
如果不存在表达式,则raise
重新引发当前作用域中处于活动状态的最后一个异常。如果当前作用域中没有活动RuntimeError
异常,则会引发异常,指示这是一个错误。
否则,raise
将第一个表达式计算为异常对象。它必须是子类或实例BaseException
。如果它是一个类,则在需要时通过实例化没有参数的类来获取异常实例。
异常的类型是异常实例的类, 值是实例本身。
通常会在引发异常时自动创建回溯对象,并将其作为__traceback__
属性附加到该属性,该属性是可写的。您可以使用with_traceback()
异常方法(返回相同的异常实例,并将其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
可以通过None
在from
子句中指定来明确禁止异常链接:
>>> >>> 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
可能只会在语法上嵌套在一个for
或 while
循环中,但不会嵌套在该循环中的函数或类定义中。
它终止最近的封闭循环,else
如果循环有一个,则跳过可选子句。
如果for
循环终止break
,则循环控制目标保持其当前值。
当break
传递控制超出try
带有finally
子句的语句时 ,该finally
子句在真正离开循环之前执行。
7.10。该continue
声明
continue_stmt :: =“继续”
continue
可能只会在语法上嵌套在一个for
或 while
循环中,但不会嵌套在该循环中的函数或类定义或 finally
子句中。它继续最近的封闭循环的下一个循环。
当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“。”)* relative_module :: =“。”* | “” + identifier module
基本的import语句(无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
如果标识符列表被star('*'
)替换,则模块中定义的所有公共名称都绑定在import
语句发生范围的本地名称空间中。
模块定义的公共名称是通过检查模块命名空间的命名空间来确定的__all__
; 如果已定义,则它必须是一个字符串序列,这些字符串是由该模块定义或导入的名称。给出的名称__all__
都被认为是公开的,并且必须存在。如果__all__
未定义,则公共名称集包括在模块命名空间中找到的所有名称,这些名称不以下划线字符('_'
)开头。__all__
应该包含整个公共API。它旨在避免意外导出不属于API的项目(例如在模块中导入和使用的库模块)。
导入的外卡形式- 只允许在模块级别使用。试图在类或函数定义中使用它会引发一个问题。from module import*
SyntaxError
指定要导入的模块时,不必指定模块的绝对名称。当模块或包包含在另一个包中时,可以在同一顶层包中进行相对导入,而不必提及包名。通过在指定模块或包中使用前导点后from
,可以指定在不指定确切名称的情况下遍历当前包层次结构的高度。一个前导点表示存在导入模块的当前包。两个点表示一个包级别。三个点是两个级别,等等。因此,如果您从包中的模块执行,那么您将最终导入。如果从内部执行,则会导入from . importmod
pkg
pkg.mod
from ..subpkg2 import mod
pkg.subpkg1
pkg.subpkg2.mod
。相对导入的规范包含在PEP 328。
importlib.import_module()
用于支持动态确定要加载的模块的应用程序。
7.11.1。未来的陈述
一个未来的声明是一个指令,一个特定的模块应该使用语法或语义会在Python的规定将来的版本在功能成为标准可被编译的编译器。
未来的声明旨在简化向未来版本的Python的迁移,从而引入对语言的不兼容更改。它允许在功能成为标准版本之前,在每个模块的基础上使用新功能。
future_stmt :: =“from”“__future__”“import” feature[“as” identifier] (“,” feature[“as” identifier])* | “from”“__future__”“import”“(” feature[“as” identifier] (“,” feature[“as” identifier])* [“,”]“)” feature :: = identifier
未来的声明必须出现在模块顶部附近。在未来声明之前可以出现的唯一行是:
- 模块docstring(如果有的话),
- 评论,
- 空白行,和
- 其他未来的陈述。
Python 3.7中唯一需要使用future语句的功能是 annotations
。
在未来的声明中启用的所有历史特色仍然被Python 3公认的名单包括absolute_import
,division
, generators
,generator_stop
,unicode_literals
, print_function
,nested_scopes
和with_statement
。它们都是冗余的,因为它们始终处于启用状态,并且只能保持向后兼容性。
未来的语句在编译时被特别识别和处理:核心构造的语义变化通常通过生成不同的代码来实现。甚至可能是新功能引入了新的不兼容语法(例如新的保留字),在这种情况下,编译器可能需要以不同方式解析模块。直到运行时才能推迟这样的决定。
对于任何给定的版本,编译器都知道已定义了哪些功能名称,如果将来的语句包含未知的功能,则会引发编译时错误。
直接运行时语义与任何import语句相同:有一个标准模块__future__
,稍后描述,它将在执行future语句时以通常的方式导入。
有趣的运行时语义取决于future语句启用的特定功能。
请注意,声明没有什么特别之处:
import __future__ [as name]
这不是未来的陈述; 它是一个普通的import语句,没有特殊的语义或语法限制。
通过调用内置函数编译的代码exec()
以及compile()
在M
包含future语句的模块中编译的代码默认情况下将使用与future语句关联的新语法或语义。这可以通过可选参数来控制compile
()
– 有关详细信息,请参阅该函数的文档。
在交互式解释器提示符下键入的未来语句将对解释器会话的其余部分生效。如果使用该-i
选项启动解释器 ,则传递脚本名称以执行,并且脚本包含future语句,它将在脚本执行后启动的交互式会话中生效。
也可以看看
- PEP 236 – 回到__future__
- __future__机制的原始提案。
7.12。该global
声明
global_stmt :: =“global” identifier(“,” identifier)*
该global
语句是一个声明,它适用于整个当前代码块。这意味着列出的标识符将被解释为全局变量。global
虽然自由变量可以引用全局变量而不被声明为全局变量,但是在没有变量的情况下分配给全局变量是不可能的 。
global
声明中列出的名称不得在该语句之前的文本中使用相同的代码块global
。
global
语句中列出的名称不得定义为形式参数或for
循环控制目标,class
定义,函数定义,import
语句或变量注释。
CPython实现细节:当前实现不强制执行某些限制,但程序不应滥用此自由,因为将来的实现可能会强制执行它们或默默地更改程序的含义。
程序员注意: global
是解析器的指令。它仅适用于与global
语句同时解析的代码。特别是,global
提供给内置exec()
函数的字符串或代码对象中包含的语句不会影响包含函数调用的代码块,并且此类字符串global
中包含的代码不受包含函数调用的代码中的语句的影响。这同样适用于eval()
和compile()
功能。
7.13。该nonlocal
声明
nonlocal_stmt :: =“nonlocal” identifier(“,” identifier)*
该nonlocal
语句使列出的标识符引用最近的封闭范围中除了全局变量之前绑定的变量。这很重要,因为绑定的默认行为是首先搜索本地名称空间。除了全局(模块)范围之外,该语句还允许封装代码重新绑定局部范围之外的变量。
nonlocal
与语句中列出的名称不同,语句中 列出的名称global
必须引用封闭范围中的预先存在的绑定(无法明确确定应创建新绑定的范围)。
nonlocal
声明中列出的名称不得与本地作用域中的预先存在的绑定冲突。
也可以看看
- PEP 3104 – 访问外部范围内的名称
nonlocal
声明的规范。