6. 表达

本章解释了Python中表达式元素的含义。

语法注释: 在本章和接下来的章节中,扩展的BNF符号将用于描述语法,而不是词汇分析。当语法规则的形式为

name ::=  othername

没有给出语义,这种形式的语义 name 和…一样 othername .

6.1. 数值型间的转换

当下面对算术运算符的描述使用短语“将数字参数转换为公共类型”时,这意味着内置类型的运算符实现的工作方式如下:

  • 如果其中一个参数是复数,则另一个参数转换为复数;

  • 否则,如果其中一个参数是浮点数,则另一个参数将转换为浮点数;

  • 否则,两者都必须是整数,不需要转换。

某些附加规则适用于某些运算符(例如,字符串作为“%”运算符的左参数)。扩展必须定义自己的转换行为。

6.2. 原子

原子是表达式中最基本的元素。最简单的原子是标识符或文字。括号、括号或大括号内的形式在语法上也被归类为原子。原子的语法是:

atom      ::=  identifier | literal | enclosure
enclosure ::=  parenth_form | list_display | dict_display | set_display
               | generator_expression | yield_atom

6.2.1. 标识符(名称)

以原子形式出现的标识符是一个名称。见节 标识符和关键字 用于词汇定义和节 名字与绑定 用于命名和绑定的文档。

当名称绑定到对象时,对原子的计算将生成该对象。当名称未绑定时,尝试对其进行计算会引发 NameError 例外。

私名管理: 当类定义中以文本形式出现的标识符以两个或多个下划线字符开头,而不是以两个或多个下划线结尾时,将其视为 private name 属于那个阶级。在为私有名称生成代码之前,私有名称将转换为较长的格式。转换在名称前面插入类名,删除前导下划线并插入一个下划线。例如,标识符 __spam 发生在一个名为 Ham 将转换为 _Ham__spam . 这种转换独立于使用标识符的语法上下文。如果转换后的名称非常长(超过255个字符),则可能发生实现定义的截断。如果类名只包含下划线,则不进行转换。

6.2.2. 直接常量

python支持字符串和字节文本以及各种数字文本:

literal ::=  stringliteral | bytesliteral
             | integer | floatnumber | imagnumber

对文本的计算将生成具有给定值的给定类型(字符串、字节、整数、浮点数、复数)的对象。该值可以在浮点和假想(复杂)文字的情况下进行近似。见节 直接常量 有关详细信息。

所有文字都对应于不可变的数据类型,因此对象的标识比其值更不重要。对具有相同值的文本(程序文本中的同一个引用或不同的引用)进行多次计算可以获得相同的对象或具有相同值的不同对象。

6.2.3. 括号表达式

带括号的表单是括在括号中的可选表达式列表:

parenth_form ::=  "(" [starred_expression] ")"

带圆括号的表达式列表生成表达式列表所生成的任何内容:如果列表至少包含一个逗号,它将生成一个元组;否则,它将生成构成表达式列表的单个表达式。

一对空括号生成一个空元组对象。由于元组是不可变的,因此应用与文本相同的规则(即,两次出现空元组可能会或可能不会产生相同的对象)。

注意,元组不是由括号组成的,而是由逗号运算符组成的。例外情况是空元组,括号 are 必需的---在表达式中允许未加修饰的“无”字会导致歧义,允许常见的拼写错误在未加修饰的情况下通过。

6.2.4. 显示列表、集合和字典

对于构建列表,集合或字典python提供了称为“displays”的特殊语法,每个语法有两种风格:

  • 容器内容被明确列出,或者

  • 它们是通过一组循环和过滤指令(称为 comprehension .

理解的常见语法元素有:

comprehension ::=  assignment_expression comp_for
comp_for      ::=  ["async"] "for" target_list "in" or_test [comp_iter]
comp_iter     ::=  comp_for | comp_if
comp_if       ::=  "if" expression_nocond [comp_iter]

理解包括一个单独的表达,后面至少有一个 for 从句和零或更多 for 或 if 条款。在这种情况下,新容器的元素是通过考虑每个 for 或 if 子句一个块,从左到右嵌套,并计算表达式以在每次到达最里面的块时生成一个元素。

但是,除了最左边的不可重复的表达式 for 子句中,在单独的隐式嵌套范围中执行理解。这样可以确保在目标列表中分配给的名称不会“泄漏”到封闭范围中。

最左边的iterable表达式 for 子句直接在封闭范围中计算,然后作为参数传递给隐式嵌套范围。后续 for 子句和最左边的任何筛选条件 for 无法在封闭范围中对子句进行计算,因为它们可能依赖于从最左边的iterable获取的值。例如: [x*y for x in range(10) for y in range(x, x+10)] .

为了确保理解总是产生一个适当类型的容器, yield 和 yield from 在隐式嵌套范围中禁止使用表达式。

从python 3.6开始, async def 函数 async for 子句可用于在 asynchronous iterator .理解 async def 函数可以包含 for 或 async for 前导表达式后面的子句可以包含 for 或 async for 条款,也可以使用 await 表达。如果理解包含 async for 子句或 await 它被称为 asynchronous comprehension . 异步理解可能会暂停它出现的协程函数的执行。也见 PEP 530 .

3.6 新版功能: 介绍了异步理解。

在 3.8 版更改: yield 和 yield from 在隐式嵌套范围中禁止。

6.2.5. 列表显示

列表显示可能是一系列用方括号括起来的空表达式:

list_display ::=  "[" [starred_list | comprehension] "]"

列表显示生成一个新的列表对象,内容由表达式列表或理解指定。当提供逗号分隔的表达式列表时,将从左到右对其元素进行计算,并按该顺序放入列表对象中。当提供了一个理解时,列表是由理解产生的元素构成的。

6.2.6. 集合显示

集合显示用大括号表示,与字典显示的区别在于缺少分隔键和值的冒号:

set_display ::=  "{" (starred_list | comprehension) "}"

集合显示生成一个新的可变集合对象,内容由表达式序列或理解指定。当提供逗号分隔的表达式列表时,将从左到右对其元素进行计算,并将其添加到集合对象中。当一个理解被提供时,这个集合是由理解产生的元素构成的。

空集不能用 {{}} ;此文本构造空字典。

6.2.7. 词典显示

字典显示可能是一组空的键/数据对,用大括号括起来:

dict_display       ::=  "{" [key_datum_list | dict_comprehension] "}"
key_datum_list     ::=  key_datum ("," key_datum)* [","]
key_datum          ::=  expression ":" expression | "**" or_expr
dict_comprehension ::=  expression ":" expression comp_for

字典显示生成新的字典对象。

如果给定了以逗号分隔的键/数据对序列,则从左到右对它们进行计算,以定义字典的条目:每个键对象都用作字典中的键,以存储相应的数据。这意味着您可以在键/数据列表中多次指定同一个键,并且该键的最终字典值将是最后一个给定的值。

双星 ** 表示 dictionary unpacking . 其操作数必须是 mapping . 每个映射项都添加到新字典中。后面的值替换先前的键/数据对和先前的字典解包设置的值。

3.5 新版功能: 解包到字典显示中,最初由 PEP 448 .

与列表式和集合式理解不同,听写式理解需要两个表达式,用冒号分隔,后面跟着常见的“for”和“if”子句。当运行理解时,生成的键和值元素按生成的顺序插入到新字典中。

对键值类型的限制在前面的章节中列出 标准类型层次结构 . (总而言之,键类型应该是 hashable ,不包括所有可变对象。)未检测到重复键之间的冲突;以为给定键值存储的最后一个数据(显示中文本最右侧)为准。

在 3.8 版更改: 在Python3.8之前,在dict理解中,键和值的评估顺序没有很好地定义。在cpython中,在键之前对值进行了评估。从3.8开始,在值之前对密钥进行评估,由 PEP 572 .

6.2.8. 生成器表达式

生成器表达式是圆括号中的紧凑生成器表示法:

generator_expression ::=  "(" expression comp_for ")"

生成器表达式生成新的生成器对象。它的语法与理解相同,只是它用括号而不是括号或大括号括起来。

生成器表达式中使用的变量在 __next__() 方法是为生成器对象调用的(方式与普通生成器相同)。然而,最左边的iterable表达式 for 立即对子句进行计算,以便在定义生成器表达式的点而不是在检索第一个值的点发出由它产生的错误。随后的 for 子句和最左边的任何筛选条件 for 无法在封闭范围中对子句进行计算,因为它们可能依赖于从最左边的iterable获取的值。例如: (x*y for x in range(10) for y in range(x, x+10)) .

对于只有一个参数的调用,可以省略括号。见节 调用 有关详细信息。

为了避免干扰生成器表达式本身的预期操作, yield 和 yield from 在隐式定义的生成器中禁止使用表达式。

如果生成器表达式包含 async for 子句或 await 它被称为 asynchronous generator expression . 异步生成器表达式返回一个新的异步生成器对象,该对象是异步迭代器(请参见 异步迭代器 )

3.6 新版功能: 介绍了异步生成器表达式。

在 3.7 版更改: 在Python3.7之前,异步生成器表达式只能出现在 async def 协同程序。从3.7开始,任何函数都可以使用异步生成器表达式。

在 3.8 版更改: yield 和 yield from 在隐式嵌套范围中禁止。

6.2.9. 返回表达式

yield_atom       ::=  "(" yield_expression ")"
yield_expression ::=  "yield" [expression_list | "from" expression]

当定义 generator 函数或 asynchronous generator 函数,因此只能在函数定义的主体中使用。在函数体中使用yield表达式将使该函数成为生成器,并将其用于 async def 函数体使协程函数成为异步生成器。例如::

def gen():  # defines a generator function
    yield 123

async def agen(): # defines an asynchronous generator function
    yield 123

由于它们对包含范围的副作用, yield 表达式不允许作为用于实现理解和生成器表达式的隐式定义范围的一部分。

在 3.8 版更改: 在用于实现理解和生成器表达式的隐式嵌套范围中禁止生成表达式。

以下描述了生成器功能,而异步生成器功能在第节中单独描述。 异步生成器功能 .

当调用生成器函数时,它返回一个称为生成器的迭代器。然后,该生成器控制生成器函数的执行。当调用生成器的一个方法时,执行开始。此时,执行继续执行第一个yield表达式,在该表达式中再次挂起,返回 expression_list 给生成器的调用者。通过suspended,我们的意思是保留所有本地状态,包括本地变量的当前绑定、指令指针、内部评估堆栈以及任何异常处理的状态。当通过调用生成器的一个方法恢复执行时,该函数可以像yield表达式只是另一个外部调用一样继续执行。恢复后的yield表达式的值取决于恢复执行的方法。如果 __next__() 使用(通常通过 for 或 next() builtin)那么结果是 None . 否则,如果 send() 则结果将是传入该方法的值。

所有这些都使得生成器函数与协程非常相似;它们产生多次,它们有多个入口点,并且可以暂停执行。唯一的区别是,生成器函数不能控制在其生成之后应继续执行的位置;该控件始终传输到生成器的调用方。

在中的任何位置都允许使用yield表达式 try 构建。如果生成器在完成之前未恢复(通过达到零引用计数或被垃圾收集),则生成器迭代器的 close() 将调用方法,允许任何挂起 finally 要执行的子句。

什么时候? yield from <expr> 它将所提供的表达式视为子迭代器。该子迭代器生成的所有值都直接传递给当前生成器方法的调用方。传入的任何值 send() 任何例外情况 throw() 如果底层迭代器具有适当的方法,则传递给它。如果不是这样,那么 send() 将提高 AttributeError 或 TypeError ,同时 throw() 将立即引发传入异常。

当底层迭代器完成时, value 凸起的属性 StopIteration 实例将成为yield表达式的值。它可以在提升时显式设置 StopIteration 或在子迭代器是生成器时自动执行(通过从子生成器返回值)。

在 3.3 版更改: 补充 yield from <expr> 将控制流委托给子迭代器。

当yield表达式是赋值语句右侧的唯一表达式时,可以省略括号。

参见

PEP 255 -简单生成器

增加生成器和 yield 语句到python。

PEP 342 -通过增强型生成器的协程

建议增强生成器的API和语法,使它们可以作为简单的协程使用。

PEP 380 -委派给子生成器的语法

提出的建议 yield_from 语法,使对子生成器的委托变得容易。

PEP 525 -异步生成器

扩大范围的提案 PEP 492 通过向协同程序函数添加生成器功能。

6.2.9.1. 生成器迭代器方法

本小节描述了生成器迭代器的方法。它们可以用来控制生成器函数的执行。

注意,当生成器已在执行时调用下面的任何生成器方法会引发 ValueError 例外。

generator.__next__()

开始执行生成器函数或在最后执行的yield表达式处继续执行。当使用 __next__() 方法,当前产量表达式的计算结果始终为 None .然后继续执行下一个yield表达式,在该表达式中生成器再次挂起,并且 expression_list 返回到 __next__() 的调用者。如果生成器退出时未生成另一个值,则 StopIteration 引发异常。

此方法通常是隐式调用的,例如 for 循环,或由内置 next() 功能。

generator.send(value)

继续执行,并将值“发送”到生成器函数中。这个 value 变元成为当前yield表达式的结果。这个 send() 方法返回生成器生成的下一个值,或引发 StopIteration 如果生成器退出而未生成另一个值。什么时候? send() 调用以启动生成器,必须使用 None 作为参数,因为没有可以接收该值的yield表达式。

generator.throw(type[, value[, traceback]])

引发类型为的异常 type 在生成器暂停时,返回生成器函数生成的下一个值。如果生成器退出时未生成另一个值,则 StopIteration 引发异常。如果生成器函数不捕获传入的异常,或引发其他异常,则该异常将传播到调用方。

generator.close()

提出一个 GeneratorExit 在生成器功能暂停的点。如果生成器功能正常退出、已关闭或引发 GeneratorExit (通过不捕获异常),关闭对调用方的返回。如果生成器产生一个值,A RuntimeError 提高了。如果生成器引发任何其他异常,则会将其传播到调用方。 close() 如果生成器因异常或正常退出而退出,则不执行任何操作。

6.2.9.2. 实例

下面是一个演示生成器和生成器函数行为的简单示例:

>>> def echo(value=None):
...     print("Execution starts when 'next()' is called for the first time.")
...     try:
...         while True:
...             try:
...                 value = (yield value)
...             except Exception as e:
...                 value = e
...     finally:
...         print("Don't forget to clean up when 'close()' is called.")
...
>>> generator = echo(1)
>>> print(next(generator))
Execution starts when 'next()' is called for the first time.
1
>>> print(next(generator))
None
>>> print(generator.send(2))
2
>>> generator.throw(TypeError, "spam")
TypeError('spam',)
>>> generator.close()
Don't forget to clean up when 'close()' is called.

例如使用 yield from 见 PEP 380:委托给子生成器的语法 在“Python的新功能”中。

6.2.9.3. 异步生成器功能

在使用 async def 进一步将函数定义为 asynchronous generator 功能。

当调用异步生成器函数时,它返回一个称为异步生成器对象的异步迭代器。然后,该对象控制生成器函数的执行。异步生成器对象通常用于 async for 协程函数中的语句,类似于生成器对象在 for 语句。

调用异步生成器的一个方法返回 awaitable 对象,并在等待此对象时开始执行。此时,执行继续执行第一个yield表达式,在该表达式中再次挂起,返回 expression_list 去等待的协同程序。与生成器一样,挂起意味着保留所有本地状态,包括本地变量的当前绑定、指令指针、内部计算堆栈以及任何异常处理的状态。当通过等待异步生成器方法返回的下一个对象来恢复执行时,该函数可以准确地继续执行,就好像yield表达式只是另一个外部调用一样。恢复后的yield表达式的值取决于恢复执行的方法。如果 __anext__() 则结果为 None .否则,如果 asend() 则结果将是传入该方法的值。

如果异步生成器碰巧在 break 、调用方任务被取消或其他异常时,生成器的异步清理代码将运行,并可能在意外上下文中引发异常或访问上下文变量--可能在它所依赖的任务的生存期之后,或者在调用异步生成器垃圾回收挂钩时的事件循环关闭期间。为避免出现这种情况,调用方必须显式关闭异步生成器,方法是调用 aclose() 方法完成生成器并最终将其与事件循环分离。

在异步生成器函数中,在 try 构建。但是,如果异步生成器在完成之前没有恢复(通过达到零引用计数或被垃圾收集),则 try 构造可能导致执行挂起失败 finally 条款。在这种情况下,运行异步生成器的事件循环或调度程序负责调用异步生成器迭代器 aclose() 方法并运行生成的协程对象,从而允许任何挂起的 finally 要执行的子句。

为了在事件循环终止时负责完成,事件循环应该定义 终结器 函数,该函数接受异步生成器-迭代器,并假定调用 aclose() 并执行协同例程。这 终结器 可以通过调用 sys.set_asyncgen_hooks() 。当第一次迭代时,异步生成器迭代器将存储已注册的 终结器 在定稿时调用。有关 终结器 方法请参见 asyncio.Loop.shutdown_asyncgens 在……里面 Lib/asyncio/base_events.py 。

表达式 yield from <expr> 在异步生成器函数中使用时出现语法错误。

6.2.9.4. 异步生成器迭代器方法

本小节描述异步生成器迭代器的方法,该迭代器用于控制生成器函数的执行。

coroutine agen.__anext__()

返回一个waitable,当run开始执行异步生成器或在最后一个执行的yield表达式处恢复它。当使用 __anext__() 方法,当前产量表达式的计算结果始终为 None 在返回的waitable中,运行时将继续到下一个yield表达式。的值 expression_list 产量表达式的值是 StopIteration 完成协同程序引发的异常。如果异步生成器退出而不生成另一个值,则awaitable将引发 StopAsyncIteration 异常,表示异步迭代已完成。

此方法通常由 async for 循环。

coroutine agen.asend(value)

返回一个waitable,当run恢复异步生成器的执行。如同 send() 方法,该方法将值“发送”到异步生成器函数,并且 value 变元成为当前yield表达式的结果。等待者返回 asend() 方法将返回生成器生成的下一个值作为引发的 StopIteration 或提高 StopAsyncIteration 如果异步生成器退出而不生成另一个值。什么时候? asend() 调用以启动异步生成器,必须使用 None 作为参数,因为没有可以接收该值的yield表达式。

coroutine agen.athrow(type[, value[, traceback]])

返回引发类型异常的可等待项 type 在异步生成器暂停时,返回生成器函数生成的下一个值,作为引发的 StopIteration 例外。如果异步生成器退出而不生成另一个值,则 StopAsyncIteration 异常由可等待的引发。如果生成器函数没有捕获传入的异常,或者引发了不同的异常,那么当运行awaitable时,该异常会传播到awaitable的调用方。

coroutine agen.aclose()

返回当run将抛出 GeneratorExit 在异步生成器函数被暂停的时候。如果异步生成器函数随后优雅地退出、已关闭或引发 GeneratorExit (不捕获异常),则返回的等待对象将引发 StopIteration 例外。对异步生成器的后续调用返回的任何进一步的等待项都将引发 StopAsyncIteration 例外。如果异步生成器产生一个值,则 RuntimeError 由等待者提出。如果异步生成器引发任何其他异常,则会将其传播到可等待的调用方。如果异步生成器已由于异常或正常退出退出,则进一步调用 aclose() 将返回一个什么都不做的等待者。

6.3. 初选

初等表示语言中最紧密的操作。它们的语法是:

primary ::=  atom | attributeref | subscription | slicing | call

6.3.1. 属性引用

属性引用是主要的,后跟句点和名称:

attributeref ::=  primary "." identifier

主对象必须计算为支持属性引用的类型的对象,而大多数对象都是这样。然后要求该对象生成名称为标识符的属性。可以通过覆盖 __getattr__() 方法。如果此属性不可用,则异常 AttributeError 提高了。否则,生成的对象的类型和值由对象确定。同一属性引用的多个计算可能产生不同的对象。

6.3.2. 订阅

序列(字符串、元组或列表)或映射(字典)对象的订阅通常从集合中选择一项:

subscription ::=  primary "[" expression_list "]"

Primary必须计算为支持订阅的对象(例如列表或字典)。用户定义的对象可以通过定义 __getitem__() 方法。

对于内置对象,有两种类型的对象支持订阅:

如果主项是映射,则表达式列表必须计算为其值是映射的键之一的对象,而订阅则选择对应于该键的映射中的值。(表达式列表是一个元组,除非它只有一个项。)

如果主项是序列,则表达式列表的计算结果必须是整数或切片(如下面一节中所讨论的)。

形式语法没有对序列中的负索引做特殊的规定;但是,内置序列都提供了 __getitem__() 通过将序列长度添加到索引来解释负索引的方法(以便 x[-1] 选择的最后一项 x )结果值必须是小于序列中项目数的非负整数,订阅将选择其索引为该值的项目(从零开始计数)。因为对负索引和切片的支持发生在对象的 __getitem__() 方法,重写此方法的子类需要显式添加该支持。

字符串的项是字符。字符不是单独的数据类型,而是由一个字符组成的字符串。

认购某些产品 classes 或 types 创建一个 generic alias 。在这种情况下,用户定义的类可以通过提供 __class_getitem__() 类方法。

6.3.3. 切屑

切片选择序列对象中的一系列项目(例如,字符串、元组或列表)。切片可以用作表达式,也可以用作赋值或 del 声明。切片的语法:

slicing      ::=  primary "[" slice_list "]"
slice_list   ::=  slice_item ("," slice_item)* [","]
slice_item   ::=  expression | proper_slice
proper_slice ::=  [lower_bound] ":" [upper_bound] [ ":" [stride] ]
lower_bound  ::=  expression
upper_bound  ::=  expression
stride       ::=  expression

这里的形式语法中存在歧义:任何看起来像表达式列表的东西也看起来像切片列表,因此任何订阅都可以解释为切片。这不是进一步复杂化语法,而是通过定义在这种情况下,作为订阅的解释优先于作为切片的解释来消除歧义(如果切片列表不包含适当的切片,则是这种情况)。

切片的语义如下。主索引(使用相同的 __getitem__() 方法作为普通订阅),使用从切片列表构造的键,如下所示。如果切片列表至少包含一个逗号,则键是包含切片项转换的元组;否则,单个切片项的转换是键。作为表达式的切片项的转换就是该表达式。适当切片的转换是一个切片对象(参见第节 标准类型层次结构 谁的 start , stop 和 step 属性是表达式的值,分别以下限、上限和跨距表示,替换 None 对于缺少的表达式。

6.3.4. 调用

调用调用可调用对象(例如 function )一系列可能是空的 arguments :

call                 ::=  primary "(" [argument_list [","] | comprehension] ")"
argument_list        ::=  positional_arguments ["," starred_and_keywords]
                            ["," keywords_arguments]
                          | starred_and_keywords ["," keywords_arguments]
                          | keywords_arguments
positional_arguments ::=  positional_item ("," positional_item)*
positional_item      ::=  assignment_expression | "*" expression
starred_and_keywords ::=  ("*" expression | keyword_item)
                          ("," "*" expression | "," keyword_item)*
keywords_arguments   ::=  (keyword_item | "**" expression)
                          ("," keyword_item | "," "**" expression)*
keyword_item         ::=  identifier "=" expression

位置参数和关键字参数后面可能存在可选的尾随逗号,但不会影响语义。

主对象必须计算为可调用对象(用户定义的函数、内置函数、内置对象的方法、类对象、类实例的方法,以及具有 __call__() 方法是可调用的)。在尝试调用之前计算所有参数表达式。请参阅章节 函数定义 对于形式的语法 parameter 列表。

如果存在关键字参数,则首先将其转换为位置参数,如下所示。首先,为形式参数创建一个未填充槽的列表。如果有n个位置参数,则将它们放置在前n个槽中。接下来,对于每个关键字参数,标识符用于确定对应的槽(如果标识符与第一个形参名称相同,则使用第一个槽,依此类推)。如果插槽已满,则 TypeError 引发异常。否则,参数的值将放在槽中,填充它(即使表达式是 None ,它将填充插槽)。处理完所有参数后,仍然未填充的槽将填充函数定义中相应的默认值。(定义函数时,只计算一次默认值;因此,用作默认值的列表或字典等可变对象将由所有调用共享,这些调用没有为相应的槽指定参数值;这通常应避免。)如果有未填充的槽,没有为其指定默认值,则 TypeError 引发异常。否则,填充槽列表将用作调用的参数列表。

CPython implementation detail: 实现可以提供位置参数没有名称的内置函数,即使它们是为了文档而“命名”的,因此不能由关键字提供。在cpython中,这是在C中实现的使用 PyArg_ParseTuple() 分析他们的参数。

如果位置参数多于形式参数槽,则 TypeError 除非使用语法的形参 *identifier 存在;在这种情况下,该形参接收包含多余位置参数的元组(如果没有多余位置参数,则为空元组)。

如果任何关键字参数与形参名称不对应,则 TypeError 除非使用语法的形参 **identifier 存在;在这种情况下,该形参接收一个字典,其中包含多余的关键字参数(使用关键字作为键,参数值作为对应值),或者如果没有多余的关键字参数,则接收一个(新的)空字典。

如果语法 *expression 出现在函数调用中, expression 必须计算为 iterable . 这些iterables中的元素被视为附加的位置参数。为了调用 f(x1, x2, *y, x3, x4) 如果 y 计算为序列 y1 … yM ,这相当于使用m+4位置参数的调用 x1 , x2 , y1 … yM , x3 , x4 .

结果是尽管 *expression 可能会出现语法 之后 显式关键字参数,它被处理 before 关键字参数(以及任何 **expression 参数——见下文)。所以::

>>> def f(a, b):
...     print(a, b)
...
>>> f(b=1, *(2,))
2 1
>>> f(a=1, *(2,))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f() got multiple values for keyword argument 'a'
>>> f(1, *(2,))
1 2

对于关键字参数和 *expression 在同一个调用中使用的语法,因此在实践中不会出现这种混淆。

如果语法 **expression 出现在函数调用中, expression 必须计算为 mapping ,其中的内容将被视为附加关键字参数。如果关键字已经存在(作为显式关键字参数,或从另一个解包中),则 TypeError 引发异常。

使用语法的形式参数 *identifier 或 **identifier 不能用作位置参数槽或关键字参数名。

在 3.5 版更改: 函数调用接受任意数量的 * 和 ** 解包,位置参数可能跟在ITerable解包之后。 (* )和关键字参数可能跟随字典解包。 (** )最初提议人 PEP 448 .

调用总是返回一些值,可能 None ,除非它引发异常。如何计算该值取决于可调用对象的类型。

如果它是---

用户定义的函数:

执行函数的代码块,并将其传递给参数列表。代码块要做的第一件事是将形式参数绑定到参数;这在第节中描述。 函数定义 . 当代码块执行 return 语句,指定函数调用的返回值。

内置函数或方法:

结果由解释器决定;请参见 内置功能 对于内置函数和方法的描述。

类对象:

将返回该类的新实例。

类实例方法:

调用相应的用户定义函数,参数列表比调用的参数列表长一个:实例成为第一个参数。

类实例:

类必须定义 __call__() 方法;然后效果与调用该方法时的效果相同。

6.4. 等待表达式

暂停执行 coroutine 关于一个 awaitable 对象。只能在 coroutine function .

await_expr ::=  "await" primary

3.5 新版功能.

6.5. 电力运营商

幂运算符比左边的一元运算符绑定得更紧密;它比右边的一元运算符绑定得更少。语法是:

power ::=  (await_expr | primary) ["**" u_expr]

因此,在未附加的幂和一元运算符序列中,从右到左对运算符进行计算(这不限制操作数的计算顺序): -1**2 结果在 -1 .

power操作符与内置操作符具有相同的语义 pow() 函数,当用两个参数调用时:它产生左参数,使其达到右参数的幂。数值参数首先转换为公共类型,结果是该类型。

对于int操作数,除非第二个参数为负数,否则结果与操作数具有相同的类型;在这种情况下,所有参数都转换为float,并传递float结果。例如, 10**2 返回 100 ,但是 10**-2 返回 0.01 .

饲养 0.0 到负功率会导致 ZeroDivisionError . 将负数提高到分数幂会导致 complex 号码。(在早期版本中,它提出了 ValueError )

6.6. 一元算术和位运算

所有一元算术和按位运算具有相同的优先级:

u_expr ::=  power | "-" u_expr | "+" u_expr | "~" u_expr

一元的 - (减号)运算符产生其数值参数的负数。

一元的 + (加号)运算符生成的数值参数不变。

一元的 ~ (invert)运算符生成其整数参数的按位反转。的位反转 x 定义为 -(x+1) . 它只适用于整数。

在这三种情况下,如果参数的类型不正确,则 TypeError 引发异常。

6.7. 二进制算术运算

二进制算术运算具有传统的优先级。注意,其中一些操作也适用于某些非数字类型。除了功率运算符,只有两个级别,一个用于乘法运算符,另一个用于加法运算符:

m_expr ::=  u_expr | m_expr "*" u_expr | m_expr "@" m_expr |
            m_expr "//" u_expr | m_expr "/" u_expr |
            m_expr "%" u_expr
a_expr ::=  m_expr | a_expr "+" m_expr | a_expr "-" m_expr

这个 * (乘法)运算符产生其参数的乘积。参数必须都是数字,或者一个参数必须是整数,另一个参数必须是序列。在前一种情况下,将数字转换为通用类型,然后相乘在一起。在后一种情况下,执行序列重复;负重复因子生成空序列。

这个 @ (at)运算符用于矩阵乘法。没有内置的python类型实现此运算符。

3.5 新版功能.

这个 / (师) // (向下取整数)运算符产生其参数的商。数值参数首先转换为公共类型。整数的除法产生一个浮点,而整数的底面除法则产生一个整数;其结果是对结果应用“floor”函数的数学除法的结果。零除提高了 ZeroDivisionError 例外。

这个 % (modulo)运算符从第一个参数除以第二个参数得到余数。数值参数首先转换为公共类型。零右参数引发 ZeroDivisionError 例外。参数可以是浮点数,例如, 3.14%0.7 等于 0.34 (自 3.14 等于 4*0.7 + 0.34 .)模运算符总是生成与第二个操作数(或零)符号相同的结果;结果的绝对值严格小于第二个操作数的绝对值。 1.

向下取整数和模块运算符通过以下标识连接: x == (x//y)*y + (x%y) . 向下取整数和模块也与内置功能相连 divmod() : divmod(x, y) == (x//y, x%y) . 2.

除了对数字执行模运算之外, % 字符串对象也会重载运算符以执行旧式字符串格式(也称为插值)。字符串格式的语法在python library reference部分中进行了描述。 printf -样式字符串格式 .

向下取整数运算符、模块运算符和 divmod() 没有为复数定义函数。相反,使用 abs() 在适当情况下发挥作用。

这个 + (加法)运算符产生其参数之和。参数要么都是数字,要么都是同一类型的序列。在前一种情况下,将数字转换为通用类型,然后将其相加。在后一种情况下,序列被连接起来。

这个 - (减法)运算符产生其参数的差。数值参数首先转换为公共类型。

6.8. 移位运算

移位操作的优先级低于算术操作:

shift_expr ::=  a_expr | shift_expr ("<<" | ">>") a_expr

这些运算符接受整数作为参数。它们根据第二个参数给出的位数将第一个参数向左或向右移动。

右移 n 位定义为向下取整数 pow(2,n) . 左移 n 位被定义为与 pow(2,n) .

6.9. 二进制位运算

三个按位操作中的每一个都具有不同的优先级:

and_expr ::=  shift_expr | and_expr "&" shift_expr
xor_expr ::=  and_expr | xor_expr "^" and_expr
or_expr  ::=  xor_expr | or_expr "|" xor_expr

这个 & 运算符生成其参数的位与,这些参数必须是整数。

这个 ^ 运算符生成其参数的位异或(exclusive or),该参数必须是整数。

这个 | 运算符生成按位(包含)或其参数,这些参数必须是整数。

6.10. 比较

与C不同,python中的所有比较操作都具有相同的优先级,这低于任何算术、移位或按位操作。与c不同的是,表达式 a < b < c 具有数学中的常规解释:

comparison    ::=  or_expr (comp_operator or_expr)*
comp_operator ::=  "<" | ">" | "==" | ">=" | "<=" | "!="
                   | "is" ["not"] | ["not"] "in"

比较产生布尔值: True 或 False .

比较可以任意链接,例如: x < y <= z 等于 x < y and y <= z 除了 y 只评估一次(但在这两种情况下 z 当 x < y 被发现是错误的)。

形式上,如果 a , b , c … y , z 是表达式和 op1 , op2 … opN 是比较运算符,那么 a op1 b op2 c ... y opN z 等于 a op1 b and b op2 c and ... y opN z ,但每个表达式最多只能计算一次。

注意 a op1 b op2 c 并不意味着在 a 和 c 例如, x < y > z 是完全合法的(也许不漂亮)。

6.10.1. 价值比较

运营商 < , > , == , >= , <= 和 != 比较两个对象的值。对象不需要具有相同的类型。

章 对象、值和类型 说明对象有一个值(除了类型和标识)。对象的值在Python中是一个相当抽象的概念:例如,对象值没有规范的访问方法。此外,不要求以特定的方式构造对象的值,例如,由对象的所有数据属性组成。比较运算符实现了一个特定的概念,即对象的值是什么。我们可以把它们看作是通过比较实现间接地定义对象的值。

因为所有类型都是(直接或间接)的子类型 object ,它们从继承默认比较行为 object . 类型可以通过实现 rich comparison methods 类似于 __lt__() ,描述在 基本定制 .

相等比较的默认行为 (== 和 != )基于对象的标识。因此,同一身份的实例的相等比较导致相等,不同身份的实例的相等比较导致不相等。这种默认行为的动机是希望所有对象都是自反的(即 x is y 暗示 x == y )

默认订单比较 (< , > , <= 和 >= )未提供;尝试引发 TypeError . 这种默认行为的一个动机是缺乏与相等类似的不变量。

默认相等比较的行为(具有不同标识的实例总是不相等)可能与具有对象值和基于值的相等的合理定义的类型相反。这样的类型将需要自定义它们的比较行为,事实上,许多内置类型已经这样做了。

下面的列表描述了最重要的内置类型的比较行为。

  • 内置数字类型的数目 (数字类型--- int , float , complex )以及标准库类型 fractions.Fraction 和 decimal.Decimal 可以在它们的类型内和类型间进行比较,但限制条件是复数不支持顺序比较。在所涉及类型的限制范围内,他们在不损失精度的情况下,在数学(算法)上比较了正确性。

    非数字值 float('NaN') 和 decimal.Decimal('NaN') 是特殊的。数字与非数字值的任何有序比较都是错误的。一个反直觉的暗示是非数字值不等于它们本身。例如,如果 x = float('NaN') , 3 < x , x < 3 和 x == x 都是假的 x != x 是真的。此行为符合IEEE 754。

  • None 和 NotImplemented 是单身汉。 PEP 8 建议始终使用 is 或 is not ,而不是相等运算符。

  • 二进制序列(的实例 bytes 或 bytearray )可以在它们的类型内部和之间进行比较。他们使用元素的数值在词典学上进行比较。

  • 字符串(的实例 str )使用数字unicode代码点(内置函数的结果)在词典中进行比较 ord() )他们的性格。 3

    不能直接比较字符串和二进制序列。

  • 序列(的实例 tuple , list 或 range )只能在每种类型中进行比较,但范围不支持顺序比较。这些类型之间的相等比较会导致不相等,而这些类型之间的排序比较会导致 TypeError .

    序列使用对应元素的比较进行词汇比较。内置容器通常假定相同的对象等于它们自己。这使得它们可以绕过对相同对象的相等性测试,以提高性能并保持其内部不变量。

    内置集合之间的词典编纂比较工作如下:

    • 对于要比较相等的两个集合,它们必须属于同一类型,具有相同的长度,并且每对对应的元素必须比较相等(例如, [1,2] == (1,2) 为false,因为类型不同)。

    • 支持顺序比较的集合的顺序与其第一个不相等元素的顺序相同(例如, [1,2,x] <= [1,2,y] 与具有相同的值 x <= y )如果不存在对应的元素,则首先对较短的集合排序(例如, [1,2] < [1,2,3] 是真的)。

  • 映射(的实例 dict )当且仅当它们相等时才比较相等 (key, value) 对。键和值的相等比较加强了自反性。

    订单比较 (< , > , <= 和 >= 提高 TypeError .

  • 集合(的实例 set 或 frozenset )可以在它们的类型内部和之间进行比较。

    它们定义了顺序比较运算符来表示子集和超集测试。这些关系不定义总订单(例如,两个集合 {{1,2}} 和 {{2,3}} 不是相等的,也不是彼此的子集,也不是彼此的超集)。因此,对于依赖于总排序的函数(例如, min() , max() 和 sorted() 给出一组未定义的结果作为输入)。

    集合的比较加强了其元素的自反性。

  • 大多数其他内置类型没有实现比较方法,因此它们继承默认的比较行为。

如果可能,自定义其比较行为的用户定义类应该遵循一些一致性规则:

  • 平等比较应该是自反的。换句话说,相同的对象应该比较相等:

    x is y implies x == y

  • 比较应该是对称的。换句话说,以下表达式应该具有相同的结果:

    x == y and y == x

    x != y and y != x

    x < y and y > x

    x <= y and y >= x

  • 比较应该是可传递的。以下(非详尽)示例说明:

    x > y and y > z implies x > z

    x < y and y <= z implies x < z

  • 反向比较应导致布尔值为负。换句话说,以下表达式应该具有相同的结果:

    x == y and not x != y

    x < y 和 not x >= y (全部订购)

    x > y 和 not x <= y (全部订购)

    最后两个表达式适用于完全有序的集合(例如序列,但不适用于集合或映射)。另请参见 total_ordering() decorator。

  • 这个 hash() 结果应该和相等的一致。相等的对象要么具有相同的hash值,要么标记为不可显示。

python不强制执行这些一致性规则。事实上,非数字值就是不遵守这些规则的一个例子。

6.10.2. 成员资格测试操作

运营商 in 和 not in 会员资格测试。 x in s 评估为 True 如果 x 是一个成员 s 和 False 否则。 x not in s 返回的否定 x in s . 所有内置序列和集合类型都支持此功能和字典,其中 in 测试字典是否具有给定的键。对于list、tuple、set、frozenset、dict或collections.deque等容器类型,表达式 x in y 等于 any(x is e or x == e for e in y) .

对于字符串和字节类型, x in y 是 True 当且仅当 x 是的子字符串 y . 等效测试是 y.find(x) != -1 . 空字符串总是被认为是任何其他字符串的子字符串,因此 "" in "abc" 将返回 True .

对于定义 __contains__() 方法, x in y 返回 True 如果 y.__contains__(x) 返回一个真值,并且 False 否则。

对于未定义的用户定义类 __contains__() 但确实要定义 __iter__() , x in y 是 True 若有价值 z ,其中表达式 x is z or x == z 为真,在迭代时生成 y . 如果在迭代过程中引发异常,就好像 in 引发了该异常。

最后,尝试使用旧式迭代协议:如果类定义 __getitem__() , x in y 是 True 如果且仅当存在非负整数索引时 i 这样的话 x is y[i] or x == y[i] ,并且没有较低的整数索引引发 IndexError 例外。(如果出现任何其他异常,就好像 in 引发了该异常)。

操作员 not in 定义为具有 in .

6.10.3. 身份比较

运营商 is 和 is not 测试对象的标识: x is y 如果且仅当 x 和 y 是同一个对象。对象的标识是使用 id() 功能。 x is not y 生成逆真值。 4

6.11. 布尔运算

or_test  ::=  and_test | or_test "or" and_test
and_test ::=  not_test | and_test "and" not_test
not_test ::=  comparison | "not" not_test

在布尔运算的上下文中,以及当控制流语句使用表达式时,以下值将被解释为假: False , None ,所有类型的数字零,以及空字符串和容器(包括字符串、元组、列表、字典、集和冻结集)。所有其他值都解释为真。用户定义的对象可以通过提供 __bool__() 方法。

操作员 not 产量 True 如果它的参数是错误的, False 否则。

表达式 x and y 第一个评估 x 如果 x 如果为false,则返回其值;否则, y 计算并返回结果值。

表达式 x or y 第一个评估 x 如果 x 为真,则返回其值;否则, y 计算并返回结果值。

注意两者都不是 and 也不 or 限制返回的值和类型 False 和 True ,但返回最后一个计算的参数。这有时是有用的,例如,如果 s 是一个字符串,如果该字符串为空,则应替换为默认值,表达式 s or 'foo' 生成所需的值。因为 not 必须创建一个新值,它将返回一个布尔值,而不考虑其参数的类型(例如, not 'foo' 生产 False 而不是 '' )

6.12. 赋值表达式

assignment_expression ::=  [identifier ":="] expression

赋值表达式(有时也称为“命名表达式”或“海象”)指定 expression 对 identifier ,同时还返回 expression .

一种常见的使用情形是在处理匹配的正则表达式时:

if matching := pattern.search(data):
    do_something(matching)

或者,在分块处理文件流时:

while chunk := file.read(9000):
    process(chunk)

3.8 新版功能: 见 PEP 572 有关赋值表达式的详细信息。

6.13. 条件表达式

conditional_expression ::=  or_test ["if" or_test "else" expression]
expression             ::=  conditional_expression | lambda_expr
expression_nocond      ::=  or_test | lambda_expr_nocond

条件表达式(有时称为“三元运算符”)在所有Python操作中优先级最低。

表达式 x if C else y 首先评估条件, C 而不是 x .如果 C 是真的, x 被计算并返回其值;否则, y 计算并返回其值。

见 PEP 308 有关条件表达式的详细信息。

6.14. 兰姆达斯

lambda_expr        ::=  "lambda" [parameter_list] ":" expression
lambda_expr_nocond ::=  "lambda" [parameter_list] ":" expression_nocond

lambda表达式(有时称为lambda形式)用于创建匿名函数。表达式 lambda parameters: expression 生成一个函数对象。未命名对象的行为类似于用以下内容定义的函数对象:

def <lambda>(parameters):
    return expression

见节 函数定义 用于参数列表的语法。请注意,使用lambda表达式创建的函数不能包含语句或批注。

6.15. 表达式列表

expression_list    ::=  expression ("," expression)* [","]
starred_list       ::=  starred_item ("," starred_item)* [","]
starred_expression ::=  expression | (starred_item ",")* [starred_item]
starred_item       ::=  assignment_expression | "*" or_expr

除非列表或集合的一部分显示,否则包含至少一个逗号的表达式列表将生成一个元组。元组的长度是列表中的表达式数。表达式从左到右进行计算。

星号 * 表示 iterable unpacking . 其操作数必须是 iterable . iterable被扩展为一系列项目,这些项目包含在解包站点的新元组、列表或集合中。

3.5 新版功能: 表达式列表中的ITerable解包,最初由 PEP 448 .

尾随逗号只需要创建一个元组(即 独生子项 );在所有其他情况下都是可选的。没有尾随逗号的单个表达式不会创建元组,而是生成该表达式的值。(要创建空元组,请使用空括号对: () )

6.16. 估价单

python从左到右计算表达式。请注意,在评估任务时,右侧在左侧之前进行评估。

在以下几行中,表达式将按其后缀的算术顺序进行计算:

expr1, expr2, expr3, expr4
(expr1, expr2, expr3, expr4)
{expr1: expr2, expr3: expr4}
expr1 + expr2 * (expr3 - expr4)
expr1(expr2, expr3, *expr4, **expr5)
expr3, expr4 = expr1, expr2

6.17. 运算符优先级

下表总结了python中的运算符优先级,从最低优先级(最少绑定)到最高优先级(最多绑定)。同一个框中的运算符具有相同的优先级。除非显式给出语法,否则运算符是二进制的。同一个框组中的运算符从左到右(求幂除外,求幂从右到左分组)。

请注意,比较、成员资格测试和身份测试具有相同的优先级,并且具有从左到右的链接功能,如中所述 比较 部分。

算符

描述

:=

赋值表达式

lambda

lambda表达式

if -- else

条件表达式

or

布尔或

and

布尔与

not x

布尔不

in, not in, is, is not, <, <=, >, >=, !=, ==

比较,包括成员资格测试和身份测试

|

按位或

^

按位异或

&

按位与

<<, >>

移位

+, -

加减法

*, @, /, //, %

乘法,矩阵乘法,除法,底除,余数 5

+x, -x, ~x

正、负、位非

**

求幂 6

await x

等待表达式

x[index], x[index:index], x(arguments...), x.attribute

订阅、切片、调用、属性引用

(expressions...) ,

[expressions...], {key: value...}, {expressions...}

绑定或括号表达式,列表显示,字典显示,设置显示

脚注

1

同时 abs(x%y) < abs(y) 在数学上是正确的,对于浮点,由于舍入,在数值上可能不是正确的。例如,假设一个平台上的python float是一个IEEE754双精度数字,那么 -1e-100 % 1e100 有相同的标志 1e100 ,计算结果为 -1e-100 + 1e100 ,数值上完全等于 1e100 . 函数 math.fmod() 返回其符号与第一个参数的符号匹配的结果,因此返回 -1e-100 在这种情况下。哪种方法更合适取决于应用程序。

2

如果x非常接近y的整数倍,则 x//y 大于 (x-x%y)//y 由于成圆。在这种情况下,python返回后一个结果,以便保留 divmod(x,y)[0] * y + x % y 非常接近 x .

3

Unicode标准区分了 code points (例如U+0041)和 abstract characters (例如,“拉丁文大写字母A”)。虽然Unicode中的大多数抽象字符只使用一个代码点表示,但还有许多抽象字符可以使用多个代码点的序列来表示。例如,抽象字符“带下加符的拉丁文大写字母C”可以表示为单个 precomposed character 在代码位置U+00C7,或作为 base character 在代码位置U+0043(拉丁文大写字母C),后跟A combining character 在代码位置U+0327(组合下加符)。

字符串上的比较运算符在Unicode代码点级别进行比较。这可能与人类的直觉相反。例如, "\u00C7" == "\u0043\u0327" 是 False 尽管这两个字符串表示相同的抽象字符“带下加符的拉丁文大写字母C”。

要在抽象字符级别比较字符串(也就是说,以人类直观的方式),请使用 unicodedata.normalize() .

4

由于自动垃圾收集、空闲列表和描述符的动态特性,在 is 运算符,例如涉及实例方法或常量之间比较的运算符。查看他们的文档了解更多信息。

5

这个 % 运算符也用于字符串格式;同样的优先级也适用。

6

电力运营商 ** 比右边的算术或位一元运算符绑定得更少,也就是说, 2**-1 是 0.5 .