6. Simple statements

简单的语句包含在单个逻辑行中。在用分号分隔的一行上可能会出现几个简单的语句。简单语句的语法为:

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. 表达 Statements

Expression 语句(通常是交互式地)用于计算和写入值,或(通常)用于调用过程(一个不返回有意义结果的函数;在 Python 中,过程返回值None)。表达式语句的其他用法是允许的,有时是有用的。表达式语句的语法为:

expression_stmt ::=  expression_list

表达式语句评估表达式列表(可以是单个表达式)。

在交互模式下,如果该值不是None,则使用内置的repr()函数将其转换为字符串,并将所产生的字符串单独写入一行的标准输出(请参见打印语句)。 (不会写入产生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节。)

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

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

将对象分配给目标列表的定义如下。

  • 如果目标列表是单个目标:将对象分配给该目标。

  • 如果目标列表是目标的逗号分隔列表:该对象必须是可迭代的,并且具有与目标列表中存在目标的项目数量相同的项目,并且这些项目从左到右分配给相应的目标。

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

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

Note

  • 如果该名称未出现在当前代码块的global语句中:该名称将绑定到当前本地名称空间中的对象。

    • 否则:名称绑定到当前全局名称空间中的对象。

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

  • 如果目标是用括号或方括号括起来的目标列表:该对象必须是可迭代的,并且具有与目标列表中存在目标的项目数量相同的项目,并且其项目从左到右分配给相应的项目。目标。

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

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

class Cls:
    x = 3             # class variable
inst = Cls()
inst.x = inst.x + 1   # writes inst.x as 4 leaving Cls.x as 3

此描述不一定适用于 Descriptors 属性,例如property()创建的属性。

  • 如果目标是预订:评估引用中的主表达式。它应该产生可变的序列对象(例如列表)或 Map 对象(例如字典)。接下来,评估下标表达式。

如果主要对象是可变序列对象(例如列表),则下标必须产生纯整数。如果为负,则将序列的长度添加到其中。结果值必须是小于序列长度的非负整数,并且要求序列将具有该索引的已分配对象分配给其对象。如果索引超出范围,则将引发IndexError(分配给下标的序列不能将新项目添加到列表中)。

如果主要对象是 Map 对象(例如字典),则下标必须具有与 Map 键类型兼容的类型,然后要求 Map 创建一个将下标 Map 到指定对象的键/基准对。这可以用相同的键值替换现有的键/值对,也可以插入新的键/值对(如果不存在具有相同值的键)。

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

CPython 实现细节: 在当前实现中,目标的语法被认为与表达式的语法相同,并且无效的语法在代码生成阶段被拒绝,从而导致不太详细的错误消息。

警告:尽管赋值的定义意味着左侧和右侧之间的重叠是“安全的”(例如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仅被评估一次。另外,在可能的情况下,实际操作会“就地”执行,这意味着不是创建新对象并将其分配给目标,而是修改了旧对象。

除了在单个语句中分配给 Tuples 和多个目标外,由扩展赋值语句完成的赋值与普通赋值的处理方式相同。类似地,除了可能的“就地”行为外,pass扩充分配执行的二进制操作与正常的二进制操作相同。

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

6.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)

这些等价假定debugAssertionError引用具有这些名称的内置变量。在当前实现中,内置变量debug在正常情况下为True,在请求优化时为False(命令行选项-O)。当在编译时请求优化时,当前代码生成器不会为 assert 语句生成任何代码。注意,没有必要在错误消息中包含失败的表达式的源代码。它会显示为堆栈跟踪的一部分。

分配给debug是非法的。内置变量的值在解释器启动时确定。

6.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)

6.5. del 语句

del_stmt ::=  "del" target_list

递归定义删除与定义分配的方式非常相似。这里有一些提示,而不是详细说明。

目标列表的删除会从左到右递归删除每个目标。

名称的删除会从本地或全局名称空间中删除该名称的绑定,具体取决于该名称是否出现在同一代码块的global语句中。如果名称未绑定,则会引发NameError异常。

如果名称作为嵌套块中的自由变量出现,则从本地名称空间中删除名称是非法的。

属性引用,订阅和切片的删除被传递给所涉及的主要对象。切片的删除通常等效于分配正确类型的空切片(但即使是由切片的对象确定)。

6.6. 打印语句

print_stmt ::=  "print" ([expression ("," expression)* [","]]
                | ">>" expression [("," expression)+ [","]])

print依次计算每个表达式,并将结果对象写入标准输出(请参见下文)。如果对象不是字符串,则首先使用字符串转换规则将其转换为字符串。然后写入(结果或原始)字符串。除非输出系统认为其位于行的开头,否则在(转换和)写入每个对象之前都要写入一个空格。这种情况是(1)当尚未将任何字符写入标准输出时;(2)当写入标准输出的最后一个字符是除' '之外的空白字符;或(3)当未对标准输出进行的最后一次写入操作未写入时print语句。 (由于某些原因,在某些情况下可以将空字符串写入标准输出.)

Note

行为类似于文件对象但不是内置文件对象的对象通常无法正确模拟文件对象行为的这一方面,因此最好不要依赖于此。

除非_语句以逗号结尾,否则将在末尾写入'\n'字符。如果语句仅包含关键字print,则这是唯一的操作。

标准输出定义为内置模块sys中名为stdout的文件对象。如果不存在这样的对象,或者它没有write()方法,则会引发RuntimeError异常。

print还具有扩展形式,由上述语法的第二部分定义。这种形式有时称为“ print V 形”。在这种形式下,>>之后的第一个表达式必须计算为“文件状”对象,特别是具有如上所述write()方法的对象。使用此扩展格式,后续表达式将打印到此文件对象。如果第一个表达式的计算结果为None,则sys.stdout用作输出文件。

6.7. return语句

return_stmt ::=  "return" [expression_list]

return只能在语法上嵌套在函数定义中,而不能在嵌套的类定义中。

如果存在表达式列表,则将对其求值,否则将替换None

return使用表达式列表(或None)作为返回值离开当前函数调用。

returnpassfinally子句将控制权从try语句中移出时,该finally子句在 true 离开函数之前被执行。

在生成器函数中,return语句不允许包含expression_list。在这种情况下,裸露的return表示生成器已完成,并将导致StopIteration升高。

6.8. yield语句

yield_stmt ::=  yield_expression

yield语句仅在定义生成器函数时使用,并且仅在生成器函数的主体中使用。在函数定义中使用yield语句足以使该定义创建生成器函数而不是普通函数。

调用生成器函数时,它将返回称为生成器迭代器或更常见的生成器的迭代器。pass重复调用生成器的next()方法来执行生成器函数的主体,直到引发异常为止。

当执行yield语句时,生成器的状态被冻结,并且expression_list的值返回给next()的调用方。 “冻结”是指保留所有局部状态,包括局部变量的当前绑定,指令指针和内部评估堆栈:保存了足够的信息,以便下次调用next()时,该函数可以完全按照如果yield语句只是另一个外部调用。

从 Python 2.5 版开始,现在tryfinally构造的try子句中允许yield语句。如果生成器在完成之前没有恢复(pass达到零引用计数或被垃圾回收),则将调用生成器迭代器的close()方法,从而允许任何未决的finally子句执行。

有关yield语义的完整详细信息,请参见Yield expressions部分。

Note

在 Python 2.2 中,仅在启用generatorsFunction时才允许yield语句。此__future__ import 语句用于启用该Function:

from __future__ import generators

See also

  • PEP 255-简单生成器

  • 向 Python 添加生成器和yield语句的建议。

  • PEP 342-pass增强型生成器的协同程序

  • 除其他生成器增强Function外,该提议还建议允许yield出现在tryfinally块中。

6.9. raise语句

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

如果不存在任何表达式,则raise重新引发当前作用域中活动的最后一个异常。如果当前范围内没有异常处于活动状态,则引发TypeError异常,表明这是一个错误(如果在 IDLE 下运行,则引发Queue.Empty异常)。

否则,raise使用None作为Ellipsis的表达式的值来计算表达式以获取三个对象。前两个对象用于确定异常的类型

如果第一个对象是实例,则异常的类型是实例的类,实例本身是值,第二个对象必须是None

如果第一个对象是一个类,它将成为异常的类型。第二个对象用于确定异常值:如果它是类的实例,则该实例成为异常值。如果第二个对象是一个 Tuples,则它用作类构造函数的参数列表;如果是None,则使用一个空的参数列表,并将任何其他对象视为构造方法的单个参数。pass调用构造函数创建的实例用作异常值。

如果存在第三个对象而不是None,则它必须是回溯对象(请参见标准类型层次结构),并且代替发生异常的位置而不是当前位置。如果存在第三个对象而不是回溯对象或None,则会引发TypeError异常。 raise的三表达式形式对于在 except 子句中透明地重新引发异常很有用,但是如果要重新引发的异常是当前作用域中最近活动的异常,则不带表达式的raise应该是首选。

有关异常的其他信息,请参见Exceptions部分,有关处理异常的信息,请参见try 语句部分。

6.10. break语句

break_stmt ::=  "break"

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

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

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

breakpassfinally子句将控制权从try语句传递出去时,该finally子句在 true 退出循环之前被执行。

6.11. continue 语句

continue_stmt ::=  "continue"

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

continuepassfinally子句将控制权从try语句中移出时,该finally子句在 true 开始下一个循环之前执行。

6.12. 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

导入语句分两个步骤执行:(1)找到一个模块,并在必要时对其进行初始化; (2)在本地名称空间(在import语句所在的范围内)中定义一个或多个名称。该语句有两种形式,即是否使用from关键字。第一种形式(不带from)对列表中的每个标识符重复这些步骤。带有from的表单执行一次步骤(1),然后重复执行步骤(2)。

要了解步骤(1)的发生方式,首先必须了解 Python 如何处理模块的分层命名。为了帮助组织模块并提供命名层次结构,Python 提供了包的概念。一个软件包可以包含其他软件包和模块,而模块不能包含其他模块或软件包。从文件系统的角度来看,包是目录,模块是文件。

一旦知道模块的名称(除非另有说明,否则术语“模块”将同时指代软件包和模块),就可以开始搜索模块或软件包。首先检查的位置是sys.modules,这是先前已导入的所有模块的缓存。如果在此处找到模块,则将其用于导入步骤(2)。

如果在高速缓存中未找到模块,则搜索sys.meta_path(可以在 PEP 302中找到sys.meta_path的规范)。该对象是finder对象的列表,将pass查询finder对象来了解它们是否知道如何pass使用模块名称调用其find_module()方法来加载模块。如果模块恰好包含在包中(由名称中的点表示),则find_module()的第二个参数作为父包中的__path__属性的值给出(直到最后一个点)以要导入的模块的名称)。如果查找程序可以找到该模块,它将返回loader(稍后讨论)或返回None

如果sys.meta_path上的所有查找器都找不到该模块,则将查询一些隐式定义的查找器。 Python 的实现在定义隐式元路径查找器方面有所不同。但是,它们都定义了一个,用于处理sys.path_hookssys.path_importer_cachesys.path

隐式查找器在两个位置之一中指定的“路径”中搜索请求的模块(“路径”不必是文件系统路径)。如果应该将要导入的模块包含在包中,则将传递给父包上find_module()__path__的第二个参数用作路径源。如果模块未包含在软件包中,则将sys.path用作路径源。

一旦选择了路径的来源,就会对其进行迭代以找到可以处理该路径的查找器。 sys.path_importer_cache上的字典会为路径缓存查找器,并检查查找器。如果路径中没有缓存查找器,则pass使用路径的单个参数调用列表中的每个对象,返回查找器或引发ImportError来搜索sys.path_hooks。如果返回了查找程序,则将其缓存在sys.path_importer_cache中,然后用于该路径条目。如果找不到找到器但路径存在,则将None值存储在sys.path_importer_cache中,以表示该路径应使用隐式的基于文件的发现器,该发现器处理作为单个文件存储的模块。如果该路径不存在,则将始终返回None的查找器放置在该路径的高速缓存中。

如果没有发现者无法找到该模块,则引发ImportError。否则,某些查找程序将返回一个加载器,该加载器的load_module()方法被调用,并带有要加载的模块的名称(有关加载器的原始定义,请参见 PEP 302)。加载程序负责在加载的模块上执行任务。首先,如果该模块已经存在于sys.modules中(如果在导入机制之外调用了加载程序,则可能),那么它将使用该模块进行初始化,而不是使用新模块。但是,如果sys.modules中不存在该模块,则应在初始化开始之前将其添加到该字典中。如果在模块加载期间发生错误,并将其添加到sys.modules,则将从 dict 中将其删除。如果发生错误,但模块已在sys.modules中,则将其保留在 dict 中。

加载程序必须在模块上设置几个属性。 __name__将设置为模块名称。除非模块是内置的(因此在sys.builtin_module_names中列出),否则__file__将成为文件的“路径”,在这种情况下,未设置属性。如果要导入的是软件包,则在查找要导入的软件包中包含的模块和软件包时,将__path__设置为要搜索的路径列表。 __package__是可选的,但应设置为包含该模块或软件包的软件包的名称(空字符串用于未包含在软件包中的模块)。 __loader__也是可选的,但应将其设置为正在加载模块的加载器对象。

如果在加载过程中发生错误,则如果尚未传播其他异常,则加载器将引发ImportError。否则,加载程序将返回已加载并初始化的模块。

当步骤(1)完成而没有引发异常时,步骤(2)可以开始。

import语句的第一种形式将本地名称空间中的模块名称绑定到模块对象,然后 continue 导入下一个标识符(如果有)。如果模块名称后跟as,则as后面的名称将用作模块的本地名称。

from表单不绑定模块名称:它会遍历标识符列表,在步骤(1)中找到的模块中查找每个标识符,然后将本地名称空间中的名称绑定到由此找到的对象。与第一种形式import一样,可以pass指定“ as localname”来提供备用的本地名称。如果找不到名称,则引发ImportError。如果标识符列表由星号('*')代替,则模块中定义的所有公共名称都将绑定在import语句的本地名称空间中。

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

带有*from形式只能出现在模块范围内。如果在函数中使用了导入通配符import *且该函数包含或是带有自由变量的嵌套块,则编译器将引发SyntaxError

指定要导入的模块时,不必指定模块的绝对名称。当一个模块或程序包包含在另一个程序包中时,可以在同一顶部程序包中进行相对导入,而不必提及程序包名称。pass在from之后的指定模块或包中使用前导点,您可以指定遍历当前包层次结构的高度,而无需指定确切的名称。一个前导点表示进行导入的模块所在的当前包。两点表示一个包装级别。三个点位于两个级别上,依此类推。因此,如果从pkg包中的模块执行from . import mod,则finally将导入pkg.mod。如果从pkg.subpkg1内部执行from ..subpkg2 import mod,则将导入pkg.subpkg2.mod。相对导入的规范包含在 PEP 328中。

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

6.12.1. FutureStatements

  • future 语句*是对编译器的指令,即应使用将来指定的 Python 版本中可用的语法或语义来编译特定模块。将来的语句旨在简化向 Python 的 Future 版本的移植,从而对语言进行不兼容的更改。它允许在该Function成为标准发布之前,按模块使用新Function。
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 的语句之前只能出现的几行是:

  • 模块文档字符串(如果有),

  • comments,

  • 空行,和

  • 其他将来的 Statements。

Python 2.6 识别的Function是unicode_literalsprint_functionabsolute_importdivisiongeneratorsnested_scopeswith_statementgeneratorswith_statementnested_scopes在 Python 2.6 及更高版本中是多余的,因为它们始终处于启用状态。

将来的语句在编译时会得到特别的识别和处理:更改核心结构的语义通常是pass生成不同的代码来实现的。甚至可能是新Function引入了新的不兼容语法(例如新的保留字)的情况,在这种情况下,编译器可能需要以不同的方式解析模块。直到运行时才能推迟此类决策。

对于任何给定的发行版,编译器都知道已定义了哪些Function名称,如果将来的语句中包含其不知道的Function,则会引发编译时错误。

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

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

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

import __future__ [as name]

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

默认情况下,在包含 future 语句的模块M中,由exec语句编译的代码或对内置函数compile()execfile()的调用将使用与该 future 语句关联的新语法或语义。从 Python 2.2 开始,这可以passcompile()的可选参数进行控制-有关详细信息,请参见该函数的文档。

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

See also

  • PEP 236-返回_Future__

  • __future_机制的原始建议。

6.13. Global 语句

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

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

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

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

CPython 实现细节: 当前实现不强制执行后两个限制,但是程序不应滥用此自由,因为将来的实现可能会强制执行它们或默默地更改程序的含义。

程序员注: global是解析器的指令。它仅适用于与global语句同时解析的代码。特别是,包含在exec语句中的global语句不会影响包含exec语句的代码块,并且包含在exec语句中的代码不受包含exec语句的代码中的global语句的影响。 eval()execfile()compile()函数也是如此。

6.14. exec 语句

exec_stmt ::=  "exec" or_expr ["in" expression ["," expression]]

该语句支持动态执行 Python 代码。第一个表达式的值应为 Unicode 字符串,* Latin-1 *编码的字符串,打开的文件对象,代码对象或 Tuples。如果是字符串,则将字符串解析为一组 Python 语句,然后执行该语句(除非发生语法错误)。 [1]如果它是一个打开的文件,则将对该文件进行解析直到 EOF 并执行。如果它是代码对象,则只需执行即可。有关 Tuples 的解释,请参见下文。在所有情况下,执行的代码都可以作为文件 Importing 有效(请参见File input部分)。请注意,即使在传递给exec语句的代码的上下文内,也不能在函数定义之外使用returnyield语句。

在所有情况下,如果Ellipsis了可选部分,则代码将在当前范围内执行。如果仅指定in之后的第一个表达式,则它应该是字典,该字典将用于全局变量和局部变量。如果给出两个表达式,它们分别用于全局变量和局部变量。如果提供,* locals 可以是任何 Map 对象。请记住,在模块级别,全局变量和局部变量是相同的字典。如果给定两个单独的对象 globals locals *,则代码将像嵌入在类定义中一样执行。

第一个表达式也可以是长度为 2 或 3 的 Tuples。在这种情况下,必须Ellipsis可选部分。形式exec(expr, globals)等效于exec expr in globals,而形式exec(expr, globals, locals)等效于exec expr in globals, localsexec的 Tuples 形式提供了与 Python 3 的兼容性,其中exec是函数而不是语句。

在版本 2.4 中进行了更改:以前,必须将* locals *作为字典。

副作用是,实现可能会将除与由执行的代码设置的变量名称相对应的键之外的其他键插入词典中。例如,当前的实现可以在键__builtins__(!)下添加对内置模块builtin的字典的引用。

程序员的提示: 内置函数eval()支持表达式的动态求值。内置函数globals()locals()分别返回当前的全局字典和局部字典,这对于传递给exec可能有用。

Footnotes

  • [1]
    • 请注意,解析器仅接受 Unix 样式的行尾约定。如果要从文件中读取代码,请确保使用universal newlines模式转换 Windows 或 Mac 风格的换行符。