6. 简单语句 — Python 文档
6. 简单的语句
简单语句包含在单个逻辑行中。 几个简单的语句可能出现在由分号分隔的一行中。 简单语句的语法是:
simple_stmt ::= expression_stmt | assert_stmt | assignment_stmt | augmented_assignment_stmt | pass_stmt | del_stmt | print_stmt | return_stmt | yield_stmt | raise_stmt | break_stmt | continue_stmt | import_stmt | future_stmt | global_stmt | exec_stmt
6.1. 表达式语句
表达式语句用于(主要以交互方式)计算和写入值,或(通常)调用过程(返回无意义结果的函数;在 Python 中,过程返回值 None
)。 表达式语句的其他用途是允许的,有时也很有用。 表达式语句的语法是:
expression_stmt ::= expression_list
表达式语句计算表达式列表(可能是单个表达式)。
在交互模式下,如果值不是 None
,则使用内置的 repr() 函数将其转换为字符串,并将结果字符串写入标准输出(请参阅第 [ X192X]打印语句) 单独一行。 (生成 None
的表达式语句没有被写入,因此过程调用不会导致任何输出。)
6.2. 赋值语句
赋值语句用于将名称(重新)绑定到值并修改可变对象的属性或项:
assignment_stmt ::= (target_list "=")+ (expression_list | yield_expression) target_list ::= target ("," target)* [","] target ::= identifier | "(" target_list ")" | "[" [target_list] "]" | attributeref | subscription | slicing
(有关最后三个符号的语法定义,请参阅 Primaries 部分。)
赋值语句评估表达式列表(请记住,这可以是单个表达式或逗号分隔的列表,后者产生一个元组)并将单个结果对象从左到右分配给每个目标列表。
分配是根据目标(列表)的形式递归定义的。 当目标是可变对象(属性引用、订阅或切片)的一部分时,可变对象必须最终执行分配并决定其有效性,如果分配不可接受,则可能引发异常。 各种类型遵守的规则和引发的异常与对象类型的定义一起给出(参见部分 标准类型层次结构 )。
将对象分配给目标列表的递归定义如下。
- 如果目标列表是单个目标:将对象分配给该目标。
- 如果目标列表是一个逗号分隔的目标列表:对象必须是一个可迭代对象,其项目数与目标列表中的目标数量相同,并且项目从左到右分配给相应的目标。
将对象分配给单个目标的递归定义如下。
如果目标是标识符(名称):
如果名称未出现在当前代码块的 global 语句中:名称绑定到当前本地命名空间中的对象。
否则:名称绑定到当前全局命名空间中的对象。
如果已经绑定,则名称为反弹。 这可能会导致先前绑定到名称的对象的引用计数达到零,从而导致对象被释放并调用其析构函数(如果有的话)。
如果目标是括在圆括号或方括号中的目标列表:该对象必须是具有与目标列表中的目标相同数量的项目的可迭代对象,并且其项目从左到右分配给相应的目标。
如果目标是属性引用: 评估引用中的主要表达式。 它应该产生一个具有可分配属性的对象; 如果不是这种情况,则会引发
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() 创建的属性。
如果目标是订阅:评估引用中的主要表达式。 它应该产生一个可变序列对象(例如列表)或映射对象(例如字典)。 接下来,评估下标表达式。
如果主是可变序列对象(例如列表),则下标必须产生一个普通整数。 如果它是负数,则将序列的长度添加到其中。 结果值必须是小于序列长度的非负整数,并且要求序列将分配的对象分配给具有该索引的项目。 如果索引超出范围,则引发
IndexError
(分配给下标序列不能向列表添加新项目)。如果主要是映射对象(例如字典),则下标必须具有与映射的键类型兼容的类型,然后要求映射创建键/数据对,将下标映射到指定的对象。 这可以用相同的键值替换现有的键/值对,或者插入一个新的键/值对(如果不存在具有相同值的键)。
如果目标是切片:评估引用中的主要表达式。 它应该产生一个可变的序列对象(例如一个列表)。 分配的对象应该是相同类型的序列对象。 接下来,计算下限和上限表达式,只要它们存在; 默认值为零和序列的长度。 边界应评估为(小)整数。 如果任一边界为负,则将序列的长度添加到其中。 结果边界被剪裁到零和序列的长度之间,包括。 最后,序列对象被要求用指定序列的项目替换切片。 如果对象允许,切片的长度可能与指定序列的长度不同,从而改变目标序列的长度。
警告:尽管赋值的定义意味着左侧和右侧之间的重叠是“安全的”(例如 a, b = b, a
交换两个变量),但在 集合中重叠了 分配给变量的数量是不安全的! 例如,以下程序打印 [0, 2]
:
x = [0, 1]
i = 0
i, x[i] = 1, 2
print x
6.2.1. 扩充赋值语句
增广赋值是在单个语句中组合二元运算和赋值语句:
augmented_assignment_stmt ::= augtarget augop (expression_list | yield_expression) augtarget ::= identifier | attributeref | subscription | slicing augop ::= "+=" | "-=" | "*=" | "/=" | "//=" | "%=" | "**=" | ">>=" | "<<=" | "&=" | "^=" | "|="
(有关最后三个符号的语法定义,请参阅 Primaries 部分。)
扩充赋值计算目标(与普通赋值语句不同,它不能是解包语句)和表达式列表,对两个操作数执行特定于赋值类型的二元运算,并将结果分配给原始目标。 目标仅评估一次。
像 x += 1
这样的增强赋值表达式可以重写为 x = x + 1
以实现类似但不完全相同的效果。 在增强版本中,x
只计算一次。 此外,如果可能,实际操作会在 就地 执行,这意味着不是创建新对象并将其分配给目标,而是修改旧对象。
除了在单个语句中为元组和多个目标赋值之外,由扩充赋值语句完成的赋值与普通赋值的处理方式相同。 类似地,除了可能的 就地 行为之外,由增广赋值执行的二元运算与正常的二元运算相同。
对于属性引用的目标,关于类和实例属性的 警告 适用于常规赋值。
6.3. 这断言陈述
断言语句是一种将调试断言插入程序的便捷方式:
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__ 的赋值是非法的。 内置变量的值在解释器启动时确定。
6.4. 这经过陈述
pass_stmt ::= "pass"
pass 是一个空操作——当它被执行时,什么也不会发生。 当语法上需要语句但不需要执行代码时,它可用作占位符,例如:
def f(arg): pass # a function that does nothing (yet)
class C: pass # a class with no methods (yet)
6.5. 这德尔陈述
del_stmt ::= "del" target_list
删除是递归定义的,与定义赋值的方式非常相似。 这里没有详细说明,而是一些提示。
删除目标列表会从左到右递归地删除每个目标。
删除名称会从本地或全局名称空间中删除该名称的绑定,具体取决于该名称是否出现在同一代码块中的 global 语句中。 如果名称未绑定,则会引发 NameError
异常。
如果某个名称作为嵌套块中的自由变量出现,则从本地命名空间中删除该名称是非法的。
属性引用、订阅和切片的删除传递给所涉及的主要对象; 切片的删除通常等同于分配正确类型的空切片(但即使这也是由切片对象决定的)。
6.6. 这打印陈述
print_stmt ::= "print" ([expression ("," expression)* [","]] | ">>" expression [("," expression)+ [","]])
print 依次计算每个表达式并将结果对象写入标准输出(见下文)。 如果对象不是字符串,则首先使用字符串转换规则将其转换为字符串。 然后写入(结果或原始)字符串。 在每个对象被(转换和)写入之前写入一个空格,除非输出系统认为它位于一行的开头。 这是 (1) 尚未将任何字符写入标准输出时的情况,(2) 当写入标准输出的最后一个字符是除 ' '
之外的空白字符时,或 (3) 最后一次写入操作时在标准输出上不是 print 语句。 (在某些情况下,出于这个原因,将空字符串写入标准输出可能会起作用。)
笔记
行为类似于文件对象但不是内置文件对象的对象通常不能正确模拟文件对象行为的这一方面,因此最好不要依赖于此。
'\n'
字符写在末尾,除非 print 语句以逗号结尾。 如果语句只包含关键字 print,这是唯一的操作。
标准输出定义为内置模块sys中名为stdout
的文件对象。 如果不存在这样的对象,或者它没有 write()
方法,则会引发 RuntimeError
异常。
print 也有一个扩展形式,由上述语法的第二部分定义。 这种形式有时被称为“print V 字形”。 在这种形式中,>>
之后的第一个表达式必须评估为“类文件”对象,特别是具有上述 write()
方法的对象。 使用此扩展形式,后续表达式将打印到此文件对象。 如果第一个表达式的计算结果为 None
,则使用 sys.stdout
作为输出文件。
6.7. 这返回陈述
return_stmt ::= "return" [expression_list]
return 只能在语法上嵌套在函数定义中出现,而不能出现在嵌套类定义中。
如果存在表达式列表,则对其求值,否则替换 None
。
return 以表达式列表(或 None
)作为返回值保留当前函数调用。
当 return 将控制权从带有 finally 子句的 try 语句传递出去时,该 finally 子句在真正离开函数之前被执行。
在生成器函数中,return 语句不允许包含 expression_list。 在这种情况下,一个空 return 表示生成器已完成并将导致 StopIteration
被提升。
6.8. 这屈服陈述
yield_stmt ::= yield_expression
yield 语句仅在定义生成器函数时使用,并且仅在生成器函数体中使用。 在函数定义中使用 yield 语句足以使该定义创建生成器函数而不是普通函数。
当一个生成器函数被调用时,它返回一个称为生成器迭代器的迭代器,或者更常见的是,一个生成器。 生成器函数的主体是通过重复调用生成器的 next() 方法来执行的,直到它引发异常。
当执行 yield 语句时,生成器的状态被冻结,并将 expression_list 的值返回给 next() 的调用者。 “冻结”是指保留所有局部状态,包括局部变量的当前绑定、指令指针和内部计算堆栈:保存足够的信息,以便下次 next() 是调用后,该函数可以像 yield 语句只是另一个外部调用一样继续执行。
从 Python 2.5 版开始,现在允许在 try … finally 构造的 try 子句中使用 yield 语句。 如果生成器在完成之前没有恢复(通过达到零引用计数或被垃圾收集),生成器迭代器的 close()
方法将被调用,允许任何挂起的 finally 子句执行。
有关 yield 语义的完整详细信息,请参阅 Yield 表达式 部分。
笔记
在 Python 2.2 中,yield 语句仅在启用 generators
功能时才允许。 此 __future__
导入语句用于启用该功能:
from __future__ import generators
6.9. 这增加陈述
raise_stmt ::= "raise" [expression ["," expression ["," expression]]]
如果不存在表达式,则 raise 重新引发当前作用域中活动的最后一个异常。 如果当前范围内没有异常处于活动状态,则会引发 TypeError
异常,表明这是一个错误(如果在 IDLE 下运行,则会引发 Queue.Empty 异常)。
否则, raise 计算表达式以获得三个对象,使用 None
作为省略表达式的值。 前两个对象用于确定异常的type和value。
如果第一个对象是实例,则异常的类型是实例的类,实例本身就是值,第二个对象必须是None
。
如果第一个对象是一个类,它就成为异常的类型。 第二个对象用于确定异常值:如果是类的实例,则该实例成为异常值。 如果第二个对象是元组,则将其用作类构造函数的参数列表; 如果是 None
,则使用空参数列表,任何其他对象都被视为构造函数的单个参数。 通过调用构造函数创建的实例用作异常值。
如果存在第三个对象而不是 None
,则它必须是回溯对象(请参阅 标准类型层次结构 部分),并且将其替换为当前位置而不是当前位置发生异常。 如果存在第三个对象而不是回溯对象或 None
,则会引发 TypeError
异常。 raise 的三表达式形式对于在 except 子句中透明地重新引发异常很有用,但是如果要重新引发的异常是当前作用域中最近的活动异常。
关于异常的更多信息可以在 Exceptions 部分找到,关于处理异常的信息在 try 语句 部分。
6.10. 这休息陈述
break_stmt ::= "break"
break 只能在语法上嵌套在 for 或 while 循环中,但不能嵌套在该循环内的函数或类定义中。
它终止最近的封闭循环,如果循环有一个,则跳过可选的 else 子句。
如果 for 循环由 break 终止,则循环控制目标保持其当前值。
当 break 将控制权从带有 finally 子句的 try 语句传递出去时,该 finally 子句在真正离开循环之前被执行。
6.11. 这继续陈述
continue_stmt ::= "continue"
continue 只能在语法上嵌套在 for 或 while 循环中,但不能嵌套在函数或类定义或 finally 子句中那个循环。 它继续最近的封闭循环的下一个循环。
当 continue 将控制权从带有 finally 子句的 try 语句传递出去时,该 finally 子句在真正开始下一个循环之前执行.
6.12. 这进口陈述
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
导入语句分两步执行:(1)找到一个模块,并在必要时对其进行初始化; (2) 在本地命名空间(发生 import 语句的作用域)中定义一个或多个名称。 该语句有两种形式,不同之处在于它是否使用 from 关键字。 第一种形式(没有 from)对列表中的每个标识符重复这些步骤。 带有from的形式执行一次步骤(1),然后重复执行步骤(2)。
要了解第 (1) 步是如何发生的,首先必须了解 Python 如何处理模块的分层命名。 为了帮助组织模块并提供命名层次结构,Python 有一个包的概念。 一个包可以包含其他包和模块,而模块不能包含其他模块或包。 从文件系统的角度来看,包是目录,模块是文件。
一旦知道模块的名称(除非另有说明,术语“模块”既指包又指模块),就可以开始搜索模块或包。 首先检查的是sys.modules,之前导入的所有模块的缓存。 如果在那里找到该模块,则在导入的步骤 (2) 中使用它。
如果在缓存中找不到该模块,则搜索 sys.meta_path(sys.meta_path 的规范可以在 PEP 302 中找到)。 该对象是一个 finder 对象的列表,通过使用模块名称调用它们的 find_module()
方法来查询它们是否知道如何加载模块。 如果模块恰好包含在一个包中(如名称中存在一个点所表示),则 find_module()
的第二个参数作为 __path__
属性的值从父包(直到被导入模块名称中最后一个点的所有内容)。 如果发现者可以找到该模块,则返回 loader(稍后讨论)或返回 None
。
如果 sys.meta_path 上的所有查找器都无法找到该模块,则会查询一些隐式定义的查找器。 Python 的实现在定义隐式元路径查找器方面有所不同。 不过,他们都定义的一个是处理 sys.path_hooks、sys.path_importer_cache 和 sys.path。
隐式查找器在两个位置之一指定的“路径”中搜索请求的模块(“路径”不必是文件系统路径)。 如果要导入的模块应该包含在包中,则传递给父包上的 find_module()
、__path__
的第二个参数用作路径的源。 如果模块未包含在包中,则使用 sys.path 作为路径源。
一旦选择了路径源,它就会迭代以找到可以处理该路径的查找器。 sys.path_importer_cache 中的 dict 缓存路径的查找器并检查查找器。 如果路径没有缓存的查找器,则通过使用路径的单个参数调用列表中的每个对象来搜索 sys.path_hooks,返回查找器或引发 ImportError
。 如果返回一个查找器,那么它会被缓存在 sys.path_importer_cache 中,然后用于该路径条目。 如果找不到查找程序但路径存在,则 None
的值存储在 sys.path_importer_cache 中,以表示处理存储为单个文件的模块的隐式基于文件的查找程序应该用于该路径。 如果该路径不存在,则始终返回 None
的查找器将放置在该路径的缓存中。
如果没有发现者可以找到模块,则 ImportError
会升起。 否则,某些查找程序返回一个加载器,其 load_module()
方法被调用,并带有要加载的模块的名称(有关加载器的原始定义,请参阅 PEP 302)。 加载器有几个职责要在它加载的模块上执行。 首先,如果该模块已经存在于 sys.modules 中(如果加载器在导入机制之外被调用),那么它将使用该模块进行初始化,而不是一个新模块。 但是,如果该模块在 sys.modules 中不存在,那么它将在初始化开始之前添加到该 dict 中。 如果在加载模块期间发生错误并将其添加到 sys.modules 中,它将从 dict 中删除。 如果发生错误但模块已经在 sys.modules 中,它会留在字典中。
加载器必须在模块上设置几个属性。 __name__
被设置为模块的名称。 __file__
是文件的“路径”,除非模块是内置的(因此在 sys.builtin_module_names 中列出)在这种情况下未设置属性。 如果正在导入的是一个包,那么 __path__
将被设置为在查找包含在被导入包中的模块和包时要搜索的路径列表。 __package__
是可选的,但应设置为包含模块或包的包的名称(空字符串用于不包含在包中的模块)。 __loader__
也是可选的,但应该设置为加载模块的加载器对象。
如果在加载过程中发生错误,那么如果其他异常尚未传播,加载器将引发 ImportError
。 否则加载器返回已加载和初始化的模块。
当步骤 (1) 完成且没有引发异常时,可以开始步骤 (2)。
import 语句的第一种形式将本地命名空间中的模块名称绑定到模块对象,然后继续导入下一个标识符(如果有)。 如果模块名称后跟 as,则将 as 后的名称用作模块的本地名称。
from 形式不绑定模块名称:它遍历标识符列表,在步骤 (1) 中找到的模块中查找每个标识符,并将本地命名空间中的名称绑定到这样找到的对象。 与 import 的第一种形式一样,可以通过指定“as localname”来提供备用本地名称。 如果未找到名称,则会引发 ImportError
。 如果标识符列表被星号 ('*'
) 替换,则模块中定义的所有公共名称都绑定在 import 语句的本地命名空间中。
模块定义的 public names 是通过检查模块的命名空间中名为 __all__
的变量来确定的; 如果已定义,则它必须是由该模块定义或导入的名称的字符串序列。 __all__
中给出的名称都被认为是公开的,并且必须存在。 如果未定义 __all__
,则公共名称集包括在模块名称空间中找到的所有名称,这些名称不以下划线字符开头 ('_'
)。 __all__
应该包含整个公共 API。 它旨在避免意外导出不属于 API 的项目(例如在模块中导入和使用的库模块)。
带有 *
的 from 形式只能出现在模块作用域中。 如果导入的通配符形式 — import *
— 用于函数中,并且该函数包含或者是具有自由变量的嵌套块,则编译器将引发 SyntaxError
。
在指定要导入的模块时,您不必指定模块的绝对名称。 当一个模块或包包含在另一个包中时,可以在同一个顶级包中进行相对导入,而无需提及包名。 通过在 from 之后的指定模块或包中使用前导点,您可以指定向上遍历当前包层次结构的高度,而无需指定确切名称。 一个前导点表示进行导入的模块所在的当前包。 两个点表示向上一层包装。 三个点上升两级,以此类推。 因此,如果您从 pkg
包中的模块执行 from . import mod
,那么您最终将导入 pkg.mod
。 如果您从 pkg.subpkg1
中执行 from ..subpkg2 import mod
,您将导入 pkg.subpkg2.mod
。 相对导入的规范包含在 PEP 328 中。
importlib.import_module() 用于支持确定需要动态加载哪些模块的应用程序。
6.12.1. 未来的陈述
future statement 是对编译器的指令,该指令应使用在指定的未来 Python 版本中可用的语法或语义来编译特定模块。 future 语句旨在简化向 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
future 语句必须出现在模块顶部附近。 可以出现在 future 语句之前的唯一行是:
- 模块文档字符串(如果有),
- 评论,
- 空行,和
- 其他未来声明。
Python 2.6 识别的特征有 unicode_literals
、print_function
、absolute_import
、division
、generators
、nested_scopes
和 [ X111X]。 generators
、with_statement
、nested_scopes
在 Python 2.6 及以上版本中是多余的,因为它们始终处于启用状态。
在编译时识别并特殊处理 future 语句:对核心构造语义的更改通常通过生成不同的代码来实现。 甚至可能出现新功能引入新的不兼容语法(例如新的保留字)的情况,在这种情况下,编译器可能需要以不同的方式解析模块。 这样的决定不能推迟到运行时。
对于任何给定的版本,编译器都知道定义了哪些功能名称,如果未来语句包含它不知道的功能,则会引发编译时错误。
直接运行时语义与任何 import 语句相同:有一个标准模块 __future__,稍后描述,它将在执行 future 语句时以通常的方式导入。
有趣的运行时语义取决于 future 语句启用的特定功能。
请注意,该语句没有什么特别之处:
import __future__ [as name]
这不是未来的声明; 它是一个普通的 import 语句,没有特殊的语义或语法限制。
由 exec 语句编译的代码或对模块 M
中发生的内置函数 compile() 和 execfile() 的调用默认情况下,包含 future 语句将使用与 future 语句关联的新语法或语义。 从 Python 2.2 开始,这可以由 compile() 的可选参数控制——有关详细信息,请参阅该函数的文档。
在交互式口译员提示下键入的未来语句将在口译员会话的其余部分生效。 如果一个解释器用 -i 选项启动,传递一个脚本名称来执行,并且脚本包含一个 future 语句,它将在脚本执行后启动的交互式会话中生效。
6.13. 这全球的陈述
global_stmt ::= "global" identifier ("," identifier)*
global 语句是适用于整个当前代码块的声明。 这意味着列出的标识符将被解释为全局变量。 没有 global 是不可能分配给全局变量的,尽管自由变量可以引用全局变量而不被声明为全局变量。
global 语句中列出的名称不得用于该 global 语句之前的同一个代码块中。
global 语句中列出的名称不得定义为形式参数或 for 循环控制目标、class 定义、函数定义或 import 声明。
程序员注: 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 an exec
statement does not affect the code block 包含 the exec
statement, and code contained in an exec
statement is unaffected by
global
statements in the code containing the exec
statement. The same applies to the eval()
, execfile()
and
compile()
functions.
6.14. 这执行陈述
exec_stmt ::= "exec" or_expr ["in" expression ["," expression]]
该语句支持 Python 代码的动态执行。 第一个表达式的计算结果应为 Unicode 字符串、Latin-1 编码字符串、打开文件对象、代码对象或元组。 如果是字符串,则该字符串将被解析为一组 Python 语句,然后执行该语句(除非发生语法错误)。 1 如果是打开的文件,则解析文件直到EOF并执行。 如果是代码对象,则简单地执行。 有关元组的解释,请参见下文。 在所有情况下,执行的代码都应作为文件输入有效(请参阅 文件输入 部分)。 请注意,即使在传递给 exec 语句的代码上下文中,也不能在函数定义之外使用 return 和 yield 语句。
在所有情况下,如果省略可选部分,代码将在当前范围内执行。 如果只指定了 in
之后的第一个表达式,它应该是一个字典,它将用于全局和局部变量。 如果给出两个表达式,它们分别用于全局变量和局部变量。 如果提供,locals 可以是任何映射对象。 请记住,在模块级别,全局变量和本地变量是同一个字典。 如果两个单独的对象被指定为 globals 和 locals,代码将被执行,就像它被嵌入到一个类定义中一样。
第一个表达式也可以是长度为 2 或 3 的元组。 在这种情况下,必须省略可选部分。 exec(expr, globals)
等价于exec expr in globals
,exec(expr, globals, locals)
等价于exec expr in globals, locals
。 exec
的元组形式提供了与 Python 3 的兼容性,其中 exec
是一个函数而不是一个语句。
2.4 版本变更: 以前, locals 必须是字典。
作为副作用,除了与由执行的代码设置的变量名称相对应的那些键之外,实现可能会在给出的字典中插入额外的键。 例如,当前实现可能会在键 __builtins__
(!) 下添加对内置模块 __builtin__ 字典的引用。
程序员提示:表达式的动态求值由内置函数eval()支持。 内置函数 globals() 和 locals() 分别返回当前的全局和局部字典,这可能有助于传递给 exec 使用]。
脚注