6. Expressions

本章介绍 Python 中表达式元素的含义。

语法 Comments: 在本章及以下各章中,将使用扩展的 BNF 表示法描述语法,而不是词法分析。语法规则的一种(替代形式)具有以下形式

name ::=  othername

并且没有给出语义,这种形式的name的语义与othername的语义相同。

6.1. 算术转换

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

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

6.2. Atoms

原子是表达式的最基本元素。最简单的原子是标识符或 Literals。括号,方括号或花括号中包含的形式在语法上也归类为原子。原子的语法为:

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

6.2.1. 标识符(名称)

作为原子出现的标识符是名称。有关词汇的定义,请参见第标识符和关键字节,有关命名和绑定的文档,请参见第命名和绑定节。

当名称绑定到对象时,对原子的评估将产生该对象。当名称未绑定时,对其求值的try会引发NameError异常。

私有名称处理: 当在类定义中按文本形式出现的标识符以两个或多个下划线字符开头且不以两个或多个下划线结尾时,则被视为该类的“私有名称”。在为专用名称生成代码之前,专用名称会转换为更长的格式。转换将在类名之前插入类名,并删除前导下划线,并插入单个下划线。例如,出现在名为Ham的类中的标识符__spam将转换为_Ham__spam。此转换独立于使用标识符的句法上下文。如果转换后的名称过长(超过 255 个字符),则可能会发生实现定义的截断。如果类名仅包含下划线,则不进行任何转换。

6.2.2. Literals

Python 支持字符串和字节 Literals 以及各种数字 Literals:

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

对 Literals 的求值会产生具有给定值的给定类型(字符串,字节,整数,浮点数,复数)的对象。对于浮点和虚(复杂)Literals,该值可以近似。有关详细信息,请参见第Literals节。

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

6.2.3. 带括号的表格

括号形式是括号中的可选表达式列表:

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

带括号的表达式列表将产生该表达式列表产生的任何结果:如果列表包含至少一个逗号,则产生一个 Tuples;否则,该表达式将产生一个 Tuples。否则,将产生组成表达式列表的单个表达式。

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

请注意,Tuples 不是由括号形成的,而是由逗号运算符形成的。空 Tuples 是一个 exception,它的圆括号是必需的*允许在表达式中使用无括号的“ nothing”会引起歧义,并允许常见的错别字pass。

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

理解包括一个表达式,后接至少一个for子句和零个或多个forif子句。在这种情况下,新容器的元素是pass将每个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子句在asynchronous iterator上进行迭代。 async def函数中的理解可以由在前导表达式之后的forasync for子句组成,可以包含其他forasync for子句,也可以使用await表达式。如果理解包含async for子句或await表达式,则称为异步理解。异步理解可能会暂停其中显示的协程函数的执行。另请参见 PEP 530

3.6 版的新Function:引入了异步理解。

在 3.8 版中进行了更改:隐式嵌套范围中禁止yieldyield from

6.2.5. Lists 显示

列表显示是括在方括号中的一系列可能为空的表达式:

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

列表显示将产生一个新的列表对象,其内容由表达式列表或理解列表指定。提供逗号分隔的表达式列表时,将按从左到右的 Sequences 评估其元素并将其按 Sequences 放入列表对象中。提供理解后,将根据理解产生的元素来构建列表。

6.2.6. 设置显示

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

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

集合显示将产生一个新的可变集合对象,其内容由一系列表达式或理解来指定。提供逗号分隔的表达式列表时,将从左到右评估其元素并将其添加到 set 对象中。提供理解时,集合是由理解产生的元素构成的。

空集不能用{}构造;此 Literals 构造了一个空字典。

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

词典显示将产生一个新的词典对象。

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

双星号**表示字典拆包。其操作数必须为mapping。每个 Map 项都添加到新词典中。较新的值将替换较早的键/基准对和较早的字典解包已设置的值。

3.5 版的新Function:最初由 PEP 448提出的解压缩到字典显示中的Function。

与 list 和 set 的理解相反,dict 的理解需要两个表达式之间用冒号分隔,后跟通常的“ for”和“ if”子句。运行理解时,将生成的键和值元素按生成 Sequences 插入新字典中。

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

在 3.8 版中进行了更改:在 python 3.8 之前的 dict 理解中,键和值的评估 Sequences 未明确定义。在 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))

仅带有一个参数的调用可以Ellipsis括号。有关详细信息,请参见第Calls节。

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

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

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

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

在 3.8 版中进行了更改:隐式嵌套范围中禁止yieldyield from

6.2.9. yield表达

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

在定义generator函数或asynchronous generator函数时使用 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 表达式。

生成器Function在下面描述,而异步生成器Function在异步 Generator Function部分中分别描述。

调用生成器函数时,它将返回称为生成器的迭代器。然后,该生成器控制生成器Function的执行。当调用生成器的方法之一时,执行开始。那时,执行前进到第一个 yield 表达式,在此处再次暂停执行,将expression_list的值返回给生成器的调用者。所谓暂停,是指保留所有局部状态,包括局部变量的当前绑定,指令指针,内部评估堆栈以及任何异常处理的状态。当pass调用生成器的方法之一恢复执行时,该函数可以完全进行,就好像 yield 表达式只是另一个外部调用一样。恢复后的 yield 表达式的值取决于恢复执行的方法。如果使用next()(通常passfornext()内置),则结果为None。否则,如果使用send(),则结果将是传递给该方法的值。

所有这些使生成器Function与协程非常相似。它们产生多次,它们具有多个入口点,并且可以暂停执行。唯一的区别是生成器函数无法控制在屈服后应该 continue 执行的位置。控件始终转移到生成器的调用方。

try构造中的任何位置都可以使用 yield 表达式。如果生成器在完成之前没有恢复(pass达到零引用计数或pass垃圾回收),则将调用生成器迭代器的close()方法,从而允许任何未决的finally子句执行。

使用yield from <expr>时,它将提供的表达式视为子迭代器。该子迭代器产生的所有值都直接传递给当前生成器方法的调用者。如果send()传入的任何值和throw()传入的任何异常,都将传递给基础迭代器(如果具有适当的方法)。如果不是这种情况,那么send()将引发AttributeErrorTypeError,而throw()将立即引发传入的异常。

当基础迭代器完成时,引发的StopIteration实例的value属性成为 yield 表达式的值。可以在引发StopIteration时显式设置,也可以在子迭代器为生成器时自动设置(pass从子生成器返回值)。

Note

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

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

See also

  • PEP 255-简单生成器

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

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

  • 该提案旨在增强生成器的 API 和语法,使其可用作简单的协程。

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

  • 引入yield_from语法的建议,使向子生成器的委派变得容易。

  • PEP 525-异步 Generator

  • pass在协程函数中添加生成器Function,该提案在 PEP 492上得到了扩展。

6.2.9.1. 生成器-迭代器方法

本小节描述了生成器迭代器的方法。它们可用于控制生成器Function的执行。

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

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

6.2.9.2. Examples

这是一个简单的示例,演示了生成器和生成器Function的行为:

>>> 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 的新增Function”中的PEP 380:委派给子生成器的语法

6.2.9.3. 异步 Generator Function

使用async def定义的函数或方法中 yield 表达式的存在进一步将该函数定义为asynchronous generator函数。

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

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

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

为了完成终结,事件循环应定义一个* finalizer 函数,该函数需要一个异步生成器-迭代器并可能调用aclose()并执行协程。该 finalizer 可以pass调用sys.set_asyncgen_hooks()进行注册。第一次迭代时,异步生成器迭代器将存储已注册的 finalizer ,以便在完成时调用。有关 finalizer *方法的参考示例,请参见Lib/asyncio/base_events.pyasyncio.Loop.shutdown_asyncgens的实现。

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

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

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

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

6.3. Primaries

原始表示语言的最紧密绑定的操作。它们的语法是:

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

6.3.1. 属性参考

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

attributeref ::=  primary "." identifier

主要对象必须对支持属性引用的类型的对象求值,大多数对象都会这样做。然后要求该对象产生名称为标识符的属性。可以pass覆盖getattr()方法来定制此产品。如果此属性不可用,则会引发异常AttributeError。否则,所生成对象的类型和值由对象确定。对同一属性引用的多次评估可能会产生不同的对象。

6.3.2. Subscriptions

订阅选择序列(字符串,Tuples 或列表)或 Map(字典)对象的项目:

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

主要对象必须评估为支持订阅的对象(例如,列表或字典)。用户定义的对象可以pass定义getitem()方法来支持预订。

对于内置对象,有两种支持预订的对象:

如果主要对象是 Map,则表达式列表必须求值对象,该对象的值是该 Map 的键之一,并且订阅将在 Map 中选择与该键对应的值。 (表达式列表是一个 Tuples,除非它只有一个项目.)

如果主变量是序列,则表达式列表必须计算为整数或切片(如下节所述)。

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

字符串的项目是字符。字符不是单独的数据类型,而是仅一个字符的字符串。

6.3.3. Slicings

切片选择序列对象中的一系列项目(例如,字符串,Tuples 或列表)。切片可用作赋值或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

此处的正式语法有歧义:看起来像表达式列表的任何内容也看起来像切片列表,因此任何订阅都可以解释为切片。pass定义在这种情况下,作为预订的解释优先于作为切片的解释,可以消除歧义,而不是进一步使语法复杂化(如果切片列表不包含适当的切片,则是这种情况)。

切片的语义如下。使用从切片列表构造的键对主要数据库进行索引(使用与普通订阅相同的getitem()方法),如下所示。如果切片列表包含至少一个逗号,则关键字为一个包含切片项目转换的 Tuples;否则为 false。否则,单独切片项的转换是关键。作为表达式的切片项的转换就是该表达式。适当切片的转换是一个切片对象(请参见标准类型层次结构),其startstopstep属性是分别以下限,上限和 Span 给出的表达式值,用None代替缺失的表达式。

6.3.4. Calls

呼叫会呼叫可能是空的arguments系列的可呼叫物件(例如function):

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()方法的对象都是可调用的)。在try调用之前,将评估所有参数表达式。请参阅Function definitions部分以获取正式parameter列表的语法。

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

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

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

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

如果语法*expression出现在函数调用中,则expression必须计算为iterable。这些可迭代元素的元素被视为附加位置参数。对于调用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必须计算为mapping,其内容被视为其他关键字参数。如果一个关键字已经存在(作为显式关键字参数,或来自另一个拆包),则会引发TypeError异常。

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

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

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

如果是-

6.4. await 表情

暂停在awaitable对象上执行coroutine。只能在coroutine function内部使用。

await_expr ::=  "await" primary

3.5 版中的新Function。

6.5. power operator

幂运算符比左侧的一元运算符更紧密地绑定。它的绑定不如一元运算符那么紧。语法为:

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

因此,在无括号的幂和一元运算符序列中,运算符从右到左进行评估(这不限制操作数的评估 Sequences):-1**2产生-1

当使用两个参数调用时,幂运算符的语义与内置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

一元-(减号)运算符产生其数字参数的取反。

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

一元~(取反)运算符产生其整数参数的按位取反。 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 版中的新Function。

/(除法)和//(底除法)运算符产生其参数的商。首先将数字参数转换为通用类型。整数除法产生浮点数,而整数除法产生整数。结果是对 math 除法运算的结果应用了“底”函数。除以零会引发ZeroDivisionError异常。

%(模)运算符从第一个参数除以第二个参数得出余数。首先将数字参数转换为通用类型。零权利参数引发ZeroDivisionError异常。参数可以是浮点数,例如3.14%0.7等于0.34(因为3.14等于4*0.7 + 0.34)。模运算符始终产生与第二个操作数相同的符号(或零);结果的绝对值严格小于第二个操作数[1]的绝对值。

楼层除法和模运算符pass以下标识连接:x == (x//y)*y + (x%y)。楼层划分和模也与内置Functiondivmod()divmod(x, y) == (x//y, x%y)连接。 [2]

除了对数字执行模运算外,字符串对象还使%运算符超载以执行旧式字符串格式化(也称为插值)。字符串格式的语法在 Python 库参考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

&运算符产生其参数的按位与,该参数必须是整数。

^运算符产生其参数的按位 XOR(异或),该值必须为整数。

|运算符产生其参数的按位(含)或,该值必须为整数。

6.10. Comparisons

与 C 不同,Python 中的所有比较运算都具有相同的优先级,该优先级低于任何算术,移位或按位运算。同样与 C 不同,像a < b < c这样的表达式具有 math 上的常规解释:

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

比较会产生布尔值:TrueFalse

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

形式上,如果* 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 中,对象的值是一个相当抽象的概念:例如,对于对象的值,没有规范的访问方法。而且,不要求对象的值应该以特定的方式构造,例如。由其所有数据属性组成。比较运算符实现了关于对象值是什么的特定概念。可以pass比较实现将它们视为间接定义对象的值。

因为所有类型都是object的(直接或间接)子类型,所以它们继承了object的默认比较行为。类型可以pass实现Basic customization中描述的* lt()之类的“丰富比较方法”来自定义其比较行为。

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

未提供默认的订单比较(<><=>=);try引发TypeError。这种默认行为的动机是缺乏与平等相似的不变性。

默认相等比较的行为,即具有不同身份的实例始终是不相等的,可能与需要对对象值和基于值的相等进行明智定义的类型形成对比。这些类型将需要自定义其比较行为,实际上,许多内置类型已经做到了。

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

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

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

序列在字典上使用相应元素的比较进行比较。内置容器通常假定相同的对象等于它们自己。这样一来,他们就可以绕过相同对象的相等性测试,以提高性能并维护其内部不变性。

内置集合之间的词典比较如下:

订单比较(<><=>=)提高TypeError

他们定义 Sequences 比较运算符来表示子集和超集测试。这些关系没有定义总的 Sequences(例如,两组{1,2}{2,3}不相等,也没有彼此的子集,也没有彼此的超集)。因此,对于依赖于总排序的函数,集合不是合适的参数(例如,在给定集合列表作为 Importing 的情况下,min()max()sorted()会产生未定义的结果)。

集的比较可增强其元素的自反性。

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

Note

x is y暗示x == y

Note

x == yy == x

x != yy != x

x < yy > x

x <= yy >= x

Note

x > y and y > z暗示x > z

x < y and y <= z暗示x < z

Note

x == ynot x != y

x < ynot x >= y(用于整体 Order)

x > ynot x <= y(用于整体 Order)

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

Python 不执行这些一致性规则。实际上,非数字值是不遵循这些规则的示例。

6.10.2. 会员资格测试操作

operatorinnot in测试成员资格。如果* x s *的成员,则x in s求值为True,否则为Falsex not in s返回x in s的否定。所有内置序列和集合类型都支持此Function以及字典,in为此字典测试字典是否具有给定的键。对于列表,Tuples,集合,frozenset,字典或 collections.deque 等容器类型,表达式x in y等效于any(x is e or x == e for e in y)

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

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

对于未定义contains()但定义iter()的用户定义的类,如果在y上进行迭代时生成了某个值z(对于表达式x is z or x == z为真),则x in yTrue。如果在迭代过程中引发了异常,则in引发了该异常。

最后,try使用旧式的迭代协议:如果一个类定义了getitem(),则且仅当存在非负整数索引* i *使得x is y[i] or x == y[i]且没有更低的整数索引引发IndexError异常时,x in y才是True。 (如果引发了任何其他异常,则in引发了该异常)。

运算符not in被定义为具有反真值in

6.10.3. 身份比较

运算符isis not测试对象的身份:x is y当且仅当* x y *是同Pair象时为 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,所有类型的数字零以及空字符串和容器(包括字符串,Tuples,列表,字典) ,集合和 Frozensets)。所有其他值均解释为 true。用户定义的对象可以pass提供bool()方法来自定义其真值。

如果其参数为假,则运算符not会产生True,否则为False

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

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

请注意,andor都不限制值并键入它们返回FalseTrue,而是返回最后一个求值的参数。有时这很有用,例如,如果s是一个字符串,如果为空,则应将其替换为默认值,表达式s or 'foo'会产生所需的值。因为not必须创建一个新值,所以无论其参数类型如何,它都返回一个布尔值(例如not 'foo'产生False而不是'')。

6.12. 赋值表达式

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

有关赋值表达式的更多详细信息,请参见 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 为 true,则评估 x 并返回其值;否则,将评估 y *并返回其值。

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

6.14. Lambdas

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

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

def <lambda>(parameters):
    return expression

有关参数列表的语法,请参见第Function definitions节。请注意,使用 lambda 表达式创建的函数不能包含语句或 Comments。

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

除了列表或集合显示的一部分外,包含至少一个逗号的表达式列表会产生一个 Tuples。Tuples 的长度是列表中表达式的数量。表达式从左到右计算。

星号*表示可重复拆包。其操作数必须为iterable。可迭代的对象被扩展为一系列项目,这些项目包括在拆包 site 的新 Tuples,列表或集合中。

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

仅需使用尾部逗号才能创建单个 Tuples(也称为* singleton *);在所有其他情况下,它都是可选的。没有尾部逗号的单个表达式不会创建 Tuples,而是产生该表达式的值。 (要创建一个空的 Tuples,请使用Pair空括号:().)

6.16. 评估 Sequences

Python 从左到右计算表达式。请注意,在评估分配时,右侧的评估先于左侧。

在以下各行中,将按后缀的算术 Sequences 对表达式求值:

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 中的运算符优先级,从最低优先级(最低绑定)到最高优先级(最高绑定)。同一框中的运算符具有相同的优先级。除非明确给出语法,否则运算符为二进制。同一框中的运算符从左到右分组(除幂运算,从右到左分组)。

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

Operator Description
:= Assignment expression
lambda Lambda expression
ifelse Conditional expression
or Boolean OR
and Boolean AND
not x Boolean NOT
in, not in, is, is not, < , <= , > , >= , != , == 比较,包括成员资格测试和身份测试
| Bitwise OR
^ Bitwise XOR
& Bitwise AND
<< , >> Shifts
+ , - 加减
* , @ , / , // , % 乘法,矩阵乘法,除法,底数除法,余数[5]
+x , -x , ~x 正,负,按位非
** Exponentiation [6]
await x Await expression
x[index] , x[index:index] , x(arguments...) , x.attribute 订阅,切片,调用,属性参考
(expressions...) ,

[expressions...]{key: value...}{expressions...}
绑定或括号表达式,列表显示,字典显示,集合显示

Footnotes

字符串上的比较运算符在 Unicode 代码点级别进行比较。这可能与人类有悖常理。例如,"\u00C7" == "\u0043\u0327"False,即使两个字符串都表示相同的抽象字符“带有 CEDILLA 的拉丁文大写字母 C”。

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

首页