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 异常。

6.2.1.1. 私有名称混淆

当在类定义中以文本形式出现的标识符以两个或多个下划线字符开头,并且不以两个或多个下划线结尾时,它将被视为该类的私有名称

另请参阅

类规范

更精确地说,私有名称在为其生成代码之前被转换为更长的形式。如果转换后的名称长度超过 255 个字符,则可能会发生实现定义的截断。

转换与标识符使用的句法上下文无关,但只有以下私有标识符会被混淆

  • 用作赋值或读取变量的名称或正在访问的属性的任何名称。

    但是,嵌套函数、类和类型别名的 __name__ 属性不会被混淆。

  • 导入的模块的名称,例如 import __spam 中的 __spam。如果模块是包的一部分(即,其名称包含一个点),则该名称不会被混淆,例如 import __foo.bar 中的 __foo 不会被混淆。

  • 导入的成员的名称,例如 from spam import __f 中的 __f

转换规则定义如下

  • 在标识符前面插入删除前导下划线并插入单个前导下划线的类名,例如,在名为 Foo_Foo__Foo 的类中出现的标识符 __spam 将转换为 _Foo__spam

  • 如果类名仅由下划线组成,则转换是标识,例如,在名为 ___ 的类中出现的标识符 __spam 将保持不变。

6.2.2. 字面值

Python 支持字符串和字节字面值以及各种数字字面值

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

对字面值的求值会产生具有给定值的给定类型(字符串、字节、整数、浮点数、复数)的对象。在浮点数和虚数(复数)字面值的情况下,该值可能是近似值。有关详细信息,请参见 字面值 部分。

所有字面值都对应于不可变的数据类型,因此对象的标识不如其值重要。对具有相同值的字面值(程序文本中的相同出现或不同的出现)进行多次求值可能会获得相同的对象或具有相同值的不同对象。

6.2.3. 圆括号形式

带括号的形式是一个用括号括起来的可选表达式列表

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

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

一对空括号会产生一个空元组对象。由于元组是不可变的,因此适用与字面值相同的规则(即,空元组的两次出现可能产生也可能不产生相同的对象)。

请注意,元组不是由括号形成的,而是由逗号的使用形成的。例外情况是空元组,对于空元组,确实需要括号 — 允许在表达式中使用不带括号的“nothing”会导致歧义,并允许常见的拼写错误被忽略。

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

为了构造列表、集合或字典,Python 提供了特殊的语法,称为“显示”,它们各有两种风格

  • 要么明确列出容器内容,要么

  • 它们是通过一组循环和过滤指令计算出来的,称为推导式

推导式的常见语法元素为

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" or_test [comp_iter]

推导式由一个表达式组成,后跟至少一个 for 子句和零个或多个 forif 子句。在这种情况下,新容器的元素是那些通过将每个 forif 子句视为块(从左到右嵌套)并求值表达式来产生元素的,每次到达最内层块时都会产生一个元素。

但是,除了最左侧 for 子句中的可迭代表达式外,推导式是在单独的隐式嵌套作用域中执行的。这确保了在目标列表中赋值的名称不会“泄漏”到封闭作用域中。

最左侧 for 子句中的可迭代表达式会在外层作用域中直接求值,然后作为参数传递给隐式嵌套的作用域。后续的 for 子句以及最左侧 for 子句中的任何过滤条件都不能在外层作用域中求值,因为它们可能依赖于从最左侧可迭代对象获得的值。例如:[x*y for x in range(10) for y in range(x, x+10)]

为了确保推导式始终产生适当类型的容器,禁止在隐式嵌套作用域中使用 yieldyield from 表达式。

自 Python 3.6 起,在 async def 函数中,可以使用 async for 子句来迭代 异步迭代器async def 函数中的推导式可以包含前导表达式之后的 forasync for 子句,还可以包含额外的 forasync for 子句,并且还可以使用 await 表达式。

如果推导式包含 async for 子句,或者除了最左侧 for 子句中的可迭代表达式之外,在任何位置包含 await 表达式或其他异步推导式,则称为异步推导式。异步推导式可能会挂起其所在的协程函数的执行。另请参阅 PEP 530

3.6 版本新增: 引入了异步推导式。

3.8 版本更改: 禁止在隐式嵌套作用域中使用 yieldyield from

3.11 版本更改: 现在允许在异步函数中的推导式内使用异步推导式。外部推导式隐式变为异步的。

6.2.5. 列表显示

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

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

列表显示会产生一个新的列表对象,其内容由一系列表达式或推导式指定。当提供以逗号分隔的表达式列表时,其元素将从左到右求值,并按该顺序放入列表对象中。当提供推导式时,列表将根据推导式产生的元素构建。

6.2.6. 集合显示

集合显示用花括号表示,并通过缺少分隔键和值的分号来与字典显示区分开。

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

集合显示会产生一个新的可变集合对象,其内容由一系列表达式或推导式指定。当提供以逗号分隔的表达式列表时,其元素将从左到右求值并添加到集合对象中。当提供推导式时,集合将根据推导式产生的元素构建。

不能使用 {} 构造空集合;此字面量构造一个空字典。

6.2.7. 字典显示

字典显示是用花括号括起来的可能为空的一系列字典项(键/值对)。

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

字典显示会产生一个新的字典对象。

如果给出以逗号分隔的字典项序列,则会从左到右求值,以定义字典的条目:每个键对象都用作字典中的键来存储相应的值。这意味着您可以在字典项列表中多次指定相同的键,并且该键的最终字典值将是最后给出的值。

双星号 ** 表示字典解包。它的操作数必须是 映射。每个映射项都会添加到新字典中。后面的值会替换较早的字典项和较早的字典解包设置的值。

3.5 版本新增: 将解包引入字典显示,最初由 PEP 448 提出。

与列表和集合推导式相反,字典推导式需要两个用冒号分隔的表达式,后跟通常的“for”和“if”子句。当推导式运行时,生成的键和值元素会按照它们产生的顺序插入新字典中。

键值的类型限制在前面的 标准类型层次结构 一节中列出。(总而言之,键类型应该是 可哈希的,这排除了所有可变对象。)不会检测重复键之间的冲突;给定键值的最后一个值(在显示中按文本最右边的值)优先。

3.8 版本更改: 在 Python 3.8 之前,在字典推导式中,键和值的求值顺序没有明确定义。在 CPython 中,值在键之前求值。从 3.8 开始,键在值之前求值,正如 PEP 572 提出的那样。

6.2.8. 生成器表达式

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

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

生成器表达式会产生一个新的生成器对象。它的语法与推导式相同,只不过它用括号而不是方括号或花括号括起来。

当为生成器对象调用 __next__() 方法时,生成器表达式中使用的变量会延迟求值(与普通生成器的方式相同)。但是,最左侧 for 子句中的可迭代表达式会立即求值,因此它产生的错误将在定义生成器表达式的点发出,而不是在检索第一个值的点发出。后续的 for 子句以及最左侧 for 子句中的任何过滤条件都不能在外层作用域中求值,因为它们可能依赖于从最左侧可迭代对象获得的值。例如:(x*y for x in range(10) for y in range(x, x+10))

在只有一个参数的调用中,可以省略括号。有关详细信息,请参阅 调用 一节。

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

如果生成器表达式包含 async for 子句或 await 表达式,则称为异步生成器表达式。异步生成器表达式返回一个新的异步生成器对象,该对象是一个异步迭代器(请参阅 异步迭代器)。

3.6 版本新增: 引入了异步生成器表达式。

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

3.8 版本更改: 禁止在隐式嵌套作用域中使用 yieldyield from

6.2.9. Yield 表达式

yield_atom       ::=  "(" yield_expression ")"
yield_from       ::=  "yield" "from" expression
yield_expression ::=  "yield" yield_list | yield_from

当定义生成器函数或异步生成器函数时,会使用 yield 表达式,因此只能在函数定义的函数体中使用。在函数体中使用 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 表达式。

生成器函数将在下面介绍,而异步生成器函数将在异步生成器函数章节中单独介绍。

当调用生成器函数时,它会返回一个称为生成器的迭代器。然后,该生成器控制生成器函数的执行。当调用生成器的方法之一时,执行开始。此时,执行会进行到第一个 yield 表达式,在那里它再次被挂起,将yield_list的值返回给生成器的调用者,如果省略了yield_list,则返回None。所谓挂起,是指保留所有局部状态,包括局部变量的当前绑定、指令指针、内部求值堆栈以及任何异常处理的状态。当通过调用生成器的方法之一恢复执行时,该函数可以像 yield 表达式只是另一个外部调用一样继续执行。恢复后,yield 表达式的值取决于恢复执行的方法。如果使用__next__()(通常通过for 或内置的next()函数),则结果为None。否则,如果使用send(),则结果将是传递给该方法的值。

所有这些都使得生成器函数与协程非常相似;它们多次 yield,它们有多个入口点,并且它们的执行可以被挂起。唯一的区别是生成器函数无法控制在 yield 后应该在哪里继续执行;控制权总是转移到生成器的调用者。

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

当使用yield from <expr>时,提供的表达式必须是可迭代的。通过迭代该可迭代对象产生的值将直接传递给当前生成器方法的调用者。任何使用send()传入的值和任何使用throw()传入的异常,如果底层迭代器具有适当的方法,则会传递给该迭代器。如果不是这种情况,那么send()将引发AttributeErrorTypeError,而throw()将立即引发传入的异常。

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

在 3.3 版本中更改: 添加了 yield from <expr> 以将控制流委托给子迭代器。

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

另请参阅

PEP 255 - 简单生成器

关于向 Python 添加生成器和yield语句的提案。

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

关于增强生成器的 API 和语法的提案,使其可用作简单的协程。

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

关于引入yield_from语法的提案,使委托给子生成器变得容易。

PEP 525 - 异步生成器

关于扩展PEP 492 的提案,通过向协程函数添加生成器功能。

6.2.9.1. 生成器迭代器方法

本小节介绍生成器迭代器的方法。它们可用于控制生成器函数的执行。

请注意,当生成器已经在执行时调用以下任何生成器方法都会引发ValueError异常。

generator.__next__()

启动生成器函数的执行或在上次执行的 yield 表达式处恢复执行。当使用__next__()方法恢复生成器函数时,当前的 yield 表达式始终求值为None。然后,执行继续到下一个 yield 表达式,生成器再次被挂起,并将yield_list的值返回给__next__()的调用者。如果生成器在没有 yield 另一个值的情况下退出,则会引发StopIteration异常。

此方法通常被隐式调用,例如通过for循环或内置的next()函数。

generator.send(value)

恢复执行并向生成器函数“发送”一个值。value 参数将成为当前 yield 表达式的结果。 send() 方法返回生成器 yield 的下一个值,如果生成器退出时没有 yield 其他值,则引发 StopIteration 异常。当调用 send() 来启动生成器时,必须使用 None 作为参数调用,因为没有 yield 表达式可以接收该值。

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

在生成器暂停的位置引发异常,并返回生成器函数 yield 的下一个值。如果生成器退出时没有 yield 其他值,则会引发 StopIteration 异常。如果生成器函数没有捕获传入的异常,或者引发了不同的异常,则该异常会传播给调用者。

在典型用法中,此调用会使用单个异常实例,类似于 raise 关键字的使用方式。

但是,为了向后兼容,支持第二个签名,该签名遵循旧版本 Python 的约定。 type 参数应该是异常类,而 value 应该是异常实例。如果未提供 value,则会调用 type 构造函数来获取一个实例。如果提供了 traceback,则会将其设置到异常上,否则可能会清除存储在 value 中的任何现有 __traceback__ 属性。

在 3.12 版本中更改: 第二个签名(type[, value[, traceback]])已弃用,并可能在未来的 Python 版本中删除。

generator.close()

在生成器函数暂停的位置引发 GeneratorExit 异常。如果生成器函数捕获了异常并返回值,则此值将从 close() 返回。如果生成器函数已关闭,或引发 GeneratorExit 异常(通过不捕获该异常),则 close() 返回 None。如果生成器 yield 一个值,则会引发 RuntimeError 异常。如果生成器引发任何其他异常,则该异常会传播给调用者。如果生成器由于异常或正常退出而已经退出,则 close() 返回 None,并且没有其他影响。

在 3.13 版本中更改: 如果生成器在关闭时返回值,则该值将由 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 的示例,请参阅 “Python 中的新特性” 中的 PEP 380:委托给子生成器的语法

6.2.9.3. 异步生成器函数

使用 async def 定义的函数或方法中存在 yield 表达式,进一步将该函数定义为异步生成器函数。

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

调用异步生成器的其中一个方法会返回一个 可等待对象,并且当等待此对象时开始执行。届时,执行将进行到第一个 yield 表达式,并在那里再次暂停,将 yield_list 的值返回给等待的协程。与生成器一样,暂停意味着保留所有局部状态,包括局部变量的当前绑定、指令指针、内部求值堆栈以及任何异常处理的状态。当通过等待异步生成器的方法返回的下一个对象恢复执行时,该函数可以像 yield 表达式只是另一个外部调用一样继续执行。恢复后 yield 表达式的值取决于恢复执行的方法。如果使用 __anext__(),则结果为 None。否则,如果使用 asend(),则结果将是传递给该方法的值。

如果异步生成器碰巧通过 break、取消调用者任务或其他异常而提前退出,则生成器的异步清理代码将运行,并可能引发异常或在意外的上下文中访问上下文变量——可能在其依赖的任务的生命周期之后,或者在调用异步生成器垃圾回收钩子的事件循环关闭期间。为了防止这种情况,调用者必须显式调用 aclose() 方法来最终完成生成器,并最终将其从事件循环中分离。

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

为了在事件循环终止时处理最终完成,事件循环应定义一个 finalizer 函数,该函数接受一个异步生成器迭代器,并推测调用 aclose() 并执行协程。可以通过调用 sys.set_asyncgen_hooks() 注册此 finalizer 。首次迭代时,异步生成器迭代器将存储注册的 finalizer,以便在最终完成时调用。有关 finalizer 方法的参考示例,请参阅 Lib/asyncio/base_events.pyasyncio.Loop.shutdown_asyncgens 的实现。

当在异步生成器函数中使用表达式 yield from <expr> 时,这是一个语法错误。

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

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

coroutine agen.__anext__()

返回一个可等待对象,当运行时,它会开始执行异步生成器,或者在上次执行的 yield 表达式处恢复它。当异步生成器函数通过 __anext__() 方法恢复时,当前 yield 表达式在返回的可等待对象中始终求值为 None,该可等待对象运行时将继续执行下一个 yield 表达式。yield 表达式的 yield_list 的值是由完成的协程引发的 StopIteration 异常的值。如果异步生成器在没有产生另一个值的情况下退出,则该可等待对象会引发 StopAsyncIteration 异常,表示异步迭代已完成。

此方法通常由 async for 循环隐式调用。

协程 agen.asend(value)

返回一个可等待对象,当运行时,它会恢复异步生成器的执行。与生成器的 send() 方法一样,此方法将一个值“发送”到异步生成器函数中,并且 value 参数成为当前 yield 表达式的结果。asend() 方法返回的可等待对象将返回生成器产生的下一个值,作为引发的 StopIteration 异常的值,如果异步生成器在没有产生另一个值的情况下退出,则会引发 StopAsyncIteration 异常。当调用 asend() 来启动异步生成器时,必须使用 None 作为参数调用,因为没有可以接收值的 yield 表达式。

协程 agen.athrow(value)
协程 agen.athrow(type[, value[, traceback]])

返回一个可等待对象,该对象在异步生成器暂停的位置引发 type 类型的异常,并返回生成器函数产生的下一个值,作为引发的 StopIteration 异常的值。如果异步生成器在没有产生另一个值的情况下退出,则该可等待对象会引发 StopAsyncIteration 异常。如果生成器函数没有捕获传入的异常,或者引发了不同的异常,那么当该可等待对象运行时,该异常将传播到该可等待对象的调用者。

在 3.12 版本中更改: 第二个签名(type[, value[, traceback]])已弃用,并可能在未来的 Python 版本中删除。

协程 agen.aclose()

返回一个可等待对象,当运行时,它会在异步生成器函数暂停的位置抛出一个 GeneratorExit 异常。如果异步生成器函数随后正常退出、已经关闭或引发 GeneratorExit 异常(通过不捕获异常),则返回的可等待对象会引发 StopIteration 异常。后续调用异步生成器返回的任何其他可等待对象都会引发 StopAsyncIteration 异常。如果异步生成器产生了一个值,则该可等待对象会引发 RuntimeError 异常。如果异步生成器引发任何其他异常,则该异常会传播到该可等待对象的调用者。如果异步生成器由于异常或正常退出而已经退出,则进一步调用 aclose() 将返回一个什么都不做的可等待对象。

6.3. 基本要素

基本要素表示语言中最紧密绑定的操作。它们的语法是

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

6.3.1. 属性引用

属性引用是一个基本要素,后跟一个句点和一个名称

attributeref ::=  primary "." identifier

基本要素必须求值为支持属性引用的类型的对象,大多数对象都支持属性引用。然后,会要求此对象生成名称为标识符的属性。产生的类型和值由对象决定。多次求值相同的属性引用可能会产生不同的对象。

可以通过重写 __getattribute__() 方法或 __getattr__() 方法来自定义此生成。首先调用 __getattribute__() 方法,如果该属性不可用,则返回一个值或引发 AttributeError

如果引发了 AttributeError,并且对象具有 __getattr__() 方法,则将调用该方法作为回退。

6.3.2. 下标

容器类实例的下标通常会从容器中选择一个元素。 泛型类的下标通常会返回一个 GenericAlias 对象。

subscription ::=  primary "[" flexible_expression_list "]"

当一个对象被下标时,解释器将求值基本要素和表达式列表。

基本要素必须求值为支持下标的对象。对象可以通过定义 __getitem__()__class_getitem__() 中的一个或两个来支持下标。当下标基本要素时,表达式列表的求值结果将被传递给这些方法之一。有关何时调用 __class_getitem__ 而不是 __getitem__ 的更多详细信息,请参阅 __class_getitem__ 与 __getitem__

如果表达式列表包含至少一个逗号,或者如果任何表达式带有星号,则表达式列表将求值为一个包含表达式列表项的 tuple。否则,表达式列表将求值为列表唯一成员的值。

在 3.11 版本中更改: 表达式列表中的表达式可以带有星号。请参阅 PEP 646

对于内置对象,有两种类型的对象通过 __getitem__() 支持下标

  1. 映射。如果基本要素是一个 映射,则表达式列表必须求值为一个其值是映射的键之一的对象,并且下标会选择映射中与该键对应的值。内置映射类的一个示例是 dict 类。

  2. 序列。如果主要对象是序列,则表达式列表必须求值为一个int或一个slice(如下节所述)。内置序列类的示例包括strlisttuple类。

正式语法没有为序列中的负索引做出特殊规定。但是,所有内置序列都提供一个__getitem__()方法,该方法通过将序列的长度添加到索引来解释负索引,例如,x[-1]选择x的最后一项。结果值必须是一个非负整数,小于序列中的项目数,并且下标选择索引值为该值的项目(从零开始计数)。由于对负索引和切片的支持发生在对象的__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__()方法)。如果切片列表至少包含一个逗号,则键是一个元组,其中包含切片项的转换;否则,单独的切片项的转换就是键。作为表达式的切片项的转换是该表达式。适当的切片的转换是一个切片对象(请参阅标准类型层次结构部分),其startstopstep属性分别是作为下界、上界和步长给定的表达式的值,如果缺少表达式则替换为None

6.3.4. 调用

调用使用一个可能为空的参数序列调用一个可调用对象(例如,一个函数

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__()方法的对象都是可调用的)。在尝试调用之前,将评估所有参数表达式。有关形式参数列表的语法,请参阅函数定义部分。

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

CPython 实现细节: 一个实现可能会提供一些内置函数,这些函数的位置参数没有名称,即使它们为了文档的目的而“命名”,因此不能通过关键字提供。在 CPython 中,对于使用PyArg_ParseTuple()来解析其参数的 C 实现的函数来说,情况就是如此。

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

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

如果语法*expression出现在函数调用中,则expression必须求值为一个可迭代对象。来自这些可迭代对象的元素被视为附加的位置参数。对于调用f(x1, x2, *y, x3, x4),如果y求值为一个序列y1,…,yM,则等效于调用 M+4 个位置参数 x1, x2, y1, …, yM, x3, x4

这样做的结果是,尽管*expression语法可能出现在显式关键字参数之后,但它会在关键字参数(以及任何**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必须求值为一个映射,其内容被视为附加的关键字参数。如果与键匹配的参数已被赋予一个值(通过显式关键字参数或来自另一个解包),则会引发TypeError异常。

当使用**expression时,此映射中的每个键都必须是一个字符串。映射中的每个值都将分配给第一个符合关键字赋值条件的形式参数,其名称与该键相同。键不必是 Python 标识符(例如,"max-temp °F"是可以接受的,尽管它不会与任何可以声明的形式参数匹配)。如果没有与形式参数匹配的项,则键值对将由**参数收集(如果存在),如果不存在,则会引发TypeError异常。

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

在 3.5 版本中更改: 函数调用接受任意数量的 *** 解包,位置参数可以跟随可迭代对象解包 (*),并且关键字参数可以跟随字典解包 (**)。最初由 PEP 448 提出。

除非引发异常,否则调用总是返回某个值,可能是 None。此值的计算方式取决于可调用对象的类型。

如果它是—

用户定义的函数

将执行该函数的代码块,并传递参数列表。代码块要做的第一件事是将形式参数绑定到实参;这在 函数定义 部分中进行了描述。当代码块执行 return 语句时,这将指定函数调用的返回值。如果执行到达代码块末尾而没有执行 return 语句,则返回值为 None

内置函数或方法

结果取决于解释器;有关内置函数和方法的描述,请参阅 内置函数

类对象

返回该类的新实例。

类实例方法

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

类实例

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

6.4. Await 表达式

暂停 协程可等待 对象上的执行。只能在 协程函数 内部使用。

await_expr ::=  "await" primary

在 3.5 版本中添加。

6.5. 幂运算符

幂运算符的绑定比其左侧的一元运算符更紧密;它比其右侧的一元运算符绑定得更松散。语法是

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

因此,在没有括号的幂运算符和一元运算符序列中,运算符从右向左求值(这不会限制操作数的求值顺序):-1**2 的结果为 -1

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

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

0.0 提升为负幂会导致 ZeroDivisionError。将负数提升为分数幂会导致 complex 数字。(在早期版本中,它引发了 ValueError。)

可以使用特殊的 __pow__()__rpow__() 方法自定义此操作。

6.6. 一元算术和按位运算

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

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

一元 -(减号)运算符产生其数值参数的否定;可以使用 __neg__() 特殊方法覆盖该操作。

一元 +(加号)运算符产生其数值参数不变;可以使用 __pos__() 特殊方法覆盖该操作。

一元 ~(取反)运算符产生其整数参数的按位取反。 x 的按位取反定义为 -(x+1)。它仅适用于整数或覆盖 __invert__() 特殊方法的自定义对象。

在所有三种情况下,如果参数没有正确的类型,则会引发 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

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

可以使用特殊的 __mul__()__rmul__() 方法自定义此操作。

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

可以使用特殊的 __matmul__()__rmatmul__() 方法自定义此操作。

在 3.5 版本中添加。

/(除法)和 //(向下取整除法)运算符产生其参数的商。数值参数首先被转换为公共类型。整数除法产生浮点数,而整数向下取整除法产生整数;结果是数学除法的结果,对该结果应用“floor”函数。除以零会引发 ZeroDivisionError 异常。

可以使用特殊的 __truediv__()__rtruediv__() 方法自定义除法运算。可以使用特殊的 __floordiv__()__rfloordiv__() 方法自定义向下取整除法运算。

%(取模)运算符产生第一个参数除以第二个参数的余数。数值参数首先被转换为公共类型。零右参数引发 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 库参考的 printf 样式字符串格式化 部分中描述。

运算可以使用特殊的 __mod__()__rmod__() 方法进行自定义。

对于复数,未定义向下取整除法运算符、模运算符和 divmod() 函数。如果适用,请使用 abs() 函数将其转换为浮点数。

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

可以使用特殊的 __add__()__radd__() 方法自定义此操作。

- (减法)运算符产生其参数之差。数字参数首先会转换为通用类型。

可以使用特殊的 __sub__()__rsub__() 方法自定义此操作。

6.8. 移位操作

移位运算的优先级低于算术运算

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

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

可以使用特殊的 __lshift__()__rlshift__() 方法自定义左移操作。可以使用特殊的 __rshift__()__rrshift__() 方法自定义右移操作。

右移 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

& 运算符产生其参数的按位与,这些参数必须是整数,或者其中一个必须是重写 __and__()__rand__() 特殊方法的自定义对象。

^ 运算符产生其参数的按位异或(独占 OR),这些参数必须是整数,或者其中一个必须是重写 __xor__()__rxor__() 特殊方法的自定义对象。

| 运算符产生其参数的按位(包括)或,这些参数必须是整数,或者其中一个必须是重写 __or__()__ror__() 特殊方法的自定义对象。

6.10. 比较

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

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

比较产生布尔值:TrueFalse。自定义的 *富比较方法* 可能会返回非布尔值。在这种情况下,Python 将在布尔上下文中对此值调用 bool()

比较可以任意链接,例如,x < y <= z 等价于 x < y and y <= z,只是 y 只被计算一次(但在两种情况下,当发现 x < y 为假时,z 都不会被计算)。

形式上,如果 abc、…、yz 是表达式,op1op2、…、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 并不意味着 ac 之间有任何形式的比较,因此,例如,x < y > z 是完全合法的(尽管可能不太好看)。

6.10.1. 值比较

运算符 <>==>=<=!= 比较两个对象的值。对象不需要具有相同的类型。

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

因为所有类型都是 object 的(直接或间接)子类型,它们从 object 继承默认的比较行为。类型可以通过实现 *富比较方法*(如 __lt__())来自定义它们的比较行为,如 基本自定义 中所述。

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

不提供默认的顺序比较(<><=>=);尝试会引发 TypeError。这种默认行为的一个动机是缺少与相等类似的不可变量。

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

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

  • 内置数值类型(数值类型 — int、float、complex)以及标准库类型 fractions.Fractiondecimal.Decimal 的数值可以在它们类型内部以及跨类型之间进行比较,但复数不支持顺序比较。在所涉及类型的限制范围内,它们的比较在数学上(算法上)是正确的,且不会损失精度。

    非数字值 float('NaN')decimal.Decimal('NaN') 是特殊的。任何数字与非数字值的有序比较都为假。一个反直觉的含义是非数字值不等于自身。例如,如果 x = float('NaN'),则 3 < xx < 3x == x 均为假,而 x != x 为真。此行为符合 IEEE 754 标准。

  • NoneNotImplemented 是单例对象。 PEP 8 建议对单例对象的比较应始终使用 isis not,绝不使用相等运算符。

  • 二进制序列(bytesbytearray 的实例)可以在其类型内部和跨类型之间进行比较。 它们使用元素的数值按字典顺序进行比较。

  • 字符串(str 的实例)使用其字符的数字 Unicode 代码点(内置函数 ord() 的结果)按字典顺序进行比较。 [3]

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

  • 序列(tuplelistrange 的实例)只能在各自类型内部进行比较,但范围不支持顺序比较。跨这些类型进行相等比较会导致不相等,跨这些类型进行排序比较会引发 TypeError

    序列使用对应元素的比较按字典顺序进行比较。内置容器通常假设相同的对象等于它们自身。这允许它们绕过对相同对象的相等性测试,以提高性能并维护其内部不变性。

    内置集合之间的字典顺序比较的工作方式如下:

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

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

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

    顺序比较(<><=>=)会引发 TypeError

  • 集合(setfrozenset 的实例)可以在其类型内部和跨类型之间进行比较。

    它们定义顺序比较运算符来表示子集和超集测试。 这些关系不定义全序(例如,两个集合 {1,2}{2,3} 不相等,也不是彼此的子集,也不是彼此的超集)。因此,集合不适合作为依赖于全序的函数的参数(例如,min()max()sorted() 在给定集合列表作为输入时会产生未定义的结果)。

    集合的比较会强制执行其元素的自反性。

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

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

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

    x is y 意味着 x == y

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

    x == yy == x

    x != yy != x

    x < yy > x

    x <= yy >= x

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

    x > y and y > z 意味着 x > z

    x < y and y <= z 意味着 x < z

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

    x == ynot x != y

    x < ynot x >= y(对于全序)

    x > ynot x <= y(对于全序)

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

  • hash() 的结果应与相等性一致。相等的对象应具有相同的哈希值,或者被标记为不可哈希。

Python 不会强制执行这些一致性规则。实际上,非数字值就是一个不遵循这些规则的例子。

6.10.2. 成员检测操作

运算符 innot in 用于测试成员关系。x in s 的求值结果为 True,如果 xs 的成员,否则为 Falsex not in s 返回 x in s 的否定。所有内置的序列和集合类型都支持此操作,字典也支持,其中 in 测试字典是否具有给定的键。对于诸如列表、元组、集合、frozenset、字典或 collections.deque 之类的容器类型,表达式 x in y 等效于 any(x is e or x == e for e in y)

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

对于定义了 __contains__() 方法的用户定义类,如果 y.__contains__(x) 返回 true 值,则 x in y 返回 True,否则返回 False

对于未定义 __contains__() 但定义了 __iter__() 的用户定义类,如果迭代 y 时生成了某个值 z,并且表达式 x is z or x == z 为 true,则 x in yTrue。如果在迭代过程中引发异常,则如同 in 引发了该异常。

最后,尝试旧式迭代协议:如果一个类定义了 __getitem__(),当且仅当存在一个非负整数索引 i,使得 x is y[i] or x == y[i],并且没有更小的整数索引引发 IndexError 异常时,x in yTrue。(如果引发任何其他异常,则如同 in 引发了该异常)。

运算符 not in 的定义为具有与 in 相反的真值。

6.10.3. 身份比较

运算符 isis not 测试对象的身份:当且仅当 xy 是同一个对象时,x is y 为 true。对象的身份使用 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:FalseNone、所有类型的数值零以及空字符串和容器(包括字符串、元组、列表、字典、集合和冻结集合)。所有其他值都解释为 true。用户定义的对象可以通过提供 __bool__() 方法来定制它们的真值。

运算符 not 如果其参数为 false,则返回 True,否则返回 False

表达式 x and y 首先计算 x;如果 x 为 false,则返回其值;否则,计算 y 并返回结果值。

表达式 x or y 首先计算 x;如果 x 为 true,则返回其值;否则,计算 y 并返回结果值。

请注意,andor 都不限制它们返回的值和类型为 FalseTrue,而是返回最后计算的参数。这有时很有用,例如,如果 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)

当赋值表达式用作表达式语句以及用作切片、条件、lambda、关键字参数和推导式 if 表达式中的子表达式以及 assertwithassignment 语句时,必须用括号括起来。在所有其他可以使用它们的地方,都不需要括号,包括在 ifwhile 语句中。

在 3.8 版本中添加: 有关赋值表达式的更多详细信息,请参阅 PEP 572

6.13. 条件表达式

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

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

表达式 x if C else y 首先计算条件 C,而不是 x。如果 C 为 true,则计算 x 并返回其值;否则,计算 y 并返回其值。

有关条件表达式的更多详细信息,请参阅 PEP 308

6.14. Lambdas

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

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

def <lambda>(parameters):
    return expression

有关参数列表的语法,请参阅 函数定义 部分。请注意,使用 lambda 表达式创建的函数不能包含语句或注释。

6.15. 表达式列表

starred_expression       ::=  ["*"] or_expr
flexible_expression      ::=  assignment_expression | starred_expression
flexible_expression_list ::=  flexible_expression ("," flexible_expression)* [","]
starred_expression_list  ::=  starred_expression ("," starred_expression)* [","]
expression_list          ::=  expression ("," expression)* [","]
yield_list               ::=  expression_list | starred_expression "," [starred_expression_list]

除非是列表或集合显示的一部分,否则包含至少一个逗号的表达式列表会产生一个元组。元组的长度是列表中表达式的数量。表达式从左到右求值。

星号 * 表示可迭代对象解包。它的操作数必须是可迭代对象。可迭代对象被展开成一系列项,这些项被包含在新元组、列表或集合中,在解包的位置。

3.5 版本新增: 表达式列表中的可迭代对象解包,最初由PEP 448 提出。

3.11 版本新增: 表达式列表中的任何项都可以使用星号。参见 PEP 646

只有创建一个单项元组时才需要尾随逗号,例如 1,;在所有其他情况下,它是可选的。不带尾随逗号的单个表达式不会创建元组,而是产生该表达式的值。(要创建一个空元组,请使用一对空括号:()。)

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 中运算符的优先级,从最高优先级(最强结合)到最低优先级(最弱结合)。同一框中的运算符具有相同的优先级。除非明确给出语法,否则运算符是二元的。同一框中的运算符从左到右分组(除了指数运算和条件表达式,它们从右到左分组)。

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

运算符

描述

(表达式...),

[表达式...]{键: 值...}{表达式...}

绑定或加括号的表达式,列表显示,字典显示,集合显示

x[索引]x[索引:索引]x(参数...)x.属性

下标,切片,调用,属性引用

await x

Await 表达式

**

指数运算 [5]

+x-x~x

正号,负号,按位 NOT

*, @, /, //, %

乘法、矩阵乘法、除法、向下取整除法、余数 [6]

+, -

加法和减法

<<, >>

移位

&

按位 AND

^

按位 XOR

|

按位 OR

innot inisis not<<=>>=!===

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

not x

布尔 NOT

and

布尔 AND

or

布尔 OR

ifelse

条件表达式

lambda

Lambda 表达式

:=

赋值表达式

脚注