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

私有变量名称的调整:当出现在类定义中的标识符以两个或多个下划线字符开始且不是以两个或多个下划线结束,它被认为是那个类的私有名称在为其生成代码之前,专用名称将转换为较长的形式。该转换插入类名称,并在名称前面删除前导下划线并插入单个下划线。例如,出现在类Ham中的标识符__spam将被转换成_Ham__spam这种变换独立于使用标识符的语法上下文。如果转换的名称非常长(长于255个字符),则可能发生实现定义的截断。如果类名称仅由下划线组成,则不进行转换。

6.2.2. 字面值

Python支持字符串类型、字节串类型,以及多种数值类型:

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

对一个字面值的求值产生一个具有给定值的给定类型(字符串、字节、整数、浮点数、复数)的对象。该值可以在浮点和虚(复)字面值的情况下近似。详情请查看字面量一下。

所有的字面值都是不可变数据类型,因此对象的ID不如它的值重要。对具有相同值(在程序文本中相同的出现或不同的出现)的字面值的多次求值可以获得相同的对象或具有相同值的不同对象。

6.2.3. 圆括号表达式

圆括号表达式是括在圆括号中的一个可选表达式序列:

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

圆括号中的表达式序列产生的就是该表达式序列产生的内容:如果序列包含至少一个逗号,那么它产生一个元组;否则,它产生组成表达式序列的那个单一表达式。

一对空圆括号产生一个空的元组对象。(即,空元组的两次出现可能产生相同或不同的对象)。

注意,元组不是由括号形成的,而是通过使用逗号操作符号。有个例外是空元组,它必须要有圆括号 — 允许表达式中出现没有括号的“空白”将导致歧义并使得常见的拼写错误无法发现。

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

Python提供一种称为“表示式”的特殊语法来构造列表、集合或字典,它们每个都有两种类型:

  • 或者容器内容被明确列出
  • 它们通过一组称为推导式的循环和过滤指令来计算。

推导式的常用语法元素是:

comprehension ::=  expression comp_for
comp_for      ::=  "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子句块产生,这些子句块从左向右嵌套,且当到达最内层的代码块时对表达式求值以产生一个列表元素。

注意,推导式在单独的作用域中执行,因此在target_list中分配的名称不会“泄漏”到外围的作用域中。

6.2.5. 列表表示式

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

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

列表表示式产生新的列表对象,内容由表达式列表或推导式指定。当提供逗号分隔的表达式列表时,其元素是从左到右的计算,并按该顺序放置到列表对象中。当提供推导式时,从推导式产生的元素构造列表。

6.2.6. 集合表示式

set表示式由大括号表示,并且通过缺少分隔键和值的冒号而与字典表示式区分开:

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

字典表示式产生一个新的字典对象。

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

双星号**表示字典分拆其操作数必须是映射每个映射项目都添加到新字典。后面的值替换早期键/数据对和早期字典解包中已经设置的值。

版本3.5中的新功能:拆分字典表示式,最初由PEP 448提出。

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

键值类型的限制在前面的标准类型层次结构中列出。(总而言之,键类型应为可哈希的,它排除所有可变对象。)未检测到重复键之间的冲突;为给定的键值存储的最后一个数据(在显示式中的文本的最右侧)占优势。

6.2.8. 生成器表达式

生成器表达式是括号中的紧凑生成器符号:

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

生成器表达式产生一个新的生成器对象。它的语法与推导式的语法相同,除了它被括在括号中而不是括号或花括号中。

当生成器对象调用__next__()方法时,生成器表达式中使用的变量将被懒惰地计算(以与正常生成器相同的方式)。但是,最左边的for子句会立即被求值,所以它产生的错误可以在生成器表达式代码中的任何其它可能的错误之前发现。后续for子句无法立即计算,因为它们可能取决于之前的for循环。例如:(x*y for x in range(10) for y in bar(x))

在只有一个参数的调用中可以省略括号。细节请参考调用一节。

6.2.9. Yield表达式

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

yield表达式仅在定义生成器函数时使用,因此只能用在函数定义的主体中。在函数体中使用yield表达式会使该函数成为生成器。

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

所有这些使生成器函数与协程非常相似;它们产生多次,它们具有多个入口点并且它们的执行可以被挂起。唯一的区别是生成器函数不能控制在它yield后继续执行的位置;控制总是转移到生成器的调用者。

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

当使用yield from <expr>时,它将提供的表达式当作是一个子迭代器。由该子迭代器产生的所有值直接传递给当前生成器的方法的调用者。使用send()传递的任何值以及使用throw()传递的任何异常都会传递给底层迭代器,如果它有适当的方法。如果不是这种情况,则send()将引发AttributeErrorTypeError,而throw只是引发立即传入的异常。

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

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

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

也可以看看

PEP 255 —— 简单的生成器
将生成器和yield语句添加到Python的提议。
PEP 342 —— 通过增强的生成器得到的协程
增强生成器的API和语法的建议,使它们可以用作简单的协程。
PEP 380 —— 用于委派给子生成器的语法
引入yield_from语法的建议,使委派给子生成器变得容易。

6.2.9.1. 生成器迭代器方法

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

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

generator.__next__()

开始执行生成器函数或在最后执行的yield表达式恢复它。当使用__next__()方法恢复生成器函数时,当前yield表达式总是计算为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(通过不捕获异常),close将返回其调用者。如果生成器产生一个值,则会引发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的示例,请参阅“Python新功能”中的PEP 380:委派给子生成器的语法

6.3. 原语

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

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

6.3.1. 属性引用

属性引用是原语后跟一个句点和一个名称:

attributeref ::=  primary "." identifier

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

6.3.2. 下标

下标选择序列(字符串,元组或列表)或映射(字典)对象的项:

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

Primary必须求值为支持下标的对象(例如列表或字典)。用户定义的对象可以通过定义__ getitem __()方法来支持下标。

对于内置对象,有两种类型的对象支持下标:

如果原语是映射,则表达式列表必须计算为值为映射的键之一的对象,并且下标选择映射中与该键对应的值。(表达式列表是一个元组,除非它只有一个项目。)

如果原语是一个序列,则表达式(列表)必须求值为整数或切片(如下一节所述)。

形式语法对序列中的负索引没有特殊规定;然而,内置序列都提供一个__ 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 __()方法),其具有从切片列表构造的键,如下。如果切片列表包含至少一个逗号,则键是包含切片项目的转换的元组;否则,单个切片项的转换是键。作为表达式的切片项目的转换是该表达式。proper_slice的转换是一个切片对象(参阅标准类型的层次),它的startstopstep属性分别是表达式lower_bound、upper_bound和stride的值,没有的表达式用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 ::=  ["*"] expression ("," ["*"] expression)*
starred_and_keywords ::=  ("*" expression | keyword_item)
                          ("," "*" expression | "," keyword_item)*
keywords_arguments   ::=  (keyword_item | "**" expression)
                          ("," keyword_item | "**" expression)*
keyword_item         ::=  identifier "=" expression

可选的尾逗号可以出现在位置和关键字参数之后,但不影响语义。

Primary必须求值为可调用对象(用户定义的函数,内置函数,内置对象的方法,类对象,类实例的方法,以及所有具有__call__()方法的对象是可调用的)。在尝试调用之前,所有参数表达式都是计算。有关形式参数列表的语法,请参见函数定义一节。

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

CPython实现细节:一个具体的实现提供的内置的函数的参数可能没有名称,即使它们在文档中有名称,因此不能由关键字提供。在CPython中,在C中使用PyArg_ParseTuple()来解析其参数的函数就是这种情况。

如果存在比形式参数槽更多的位置参数,则引发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 ?
TypeError: f() got multiple values for keyword argument 'a'
>>> f(1, *(2,))
1 2

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

如果语法**expression出现在函数调用中,则expression必须求值为一个映射,其内容被视为额外的关键字参数。如果某个关键字已存在(作为显式关键字参数或来自其他拆分操作),则会引发TypeError异常。

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

在版本3.5中更改:函数调用接受任意数量的***分拆操作,位置参数可以跟随可迭代的分拆(*),关键字参数可以跟随字典分拆(**)。最初由PEP 448提议。

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

如果是-

用户定义的函数:

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

内建函数或方法:

结果取决于翻译;有关内置函数和方法的说明,请参见内置函数

一个类对象:

返回该类的一个新实例。

a class实例方法:

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

a class实例:

该类必须定义一个__ call __()方法;效果与调用该方法时的效果相同。

6.4. await表达式

挂起awaitable对象上协程的执行。只能在协程函数内使用。

await_expr ::=  "await" primary

版本3.5中的新功能。

6.5. 幂操作符

幂操作符比其左边的一元操作符绑定高;比其右边的一元操作符绑定性低。语法是:

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

因此,在未加括号的幂和一元操作符序列中,从右到左对操作进行求值(这不限制操作数的求值顺序):-1**2的结果是-1

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

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

0.0的负数次幂导致ZeroDivisionError负数的分数幂会导致复数(在早期版本中,它引发一个ValueError。)

6.6. 一元算术和位操作

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

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

一元-(minus)操作符产生其数值参数的否定。

一元+(plus)操作符原样产生其数值参数。

一元的(反转)操作符产生其整数参数的按位反转。x的按位反转定义为 -(x1)它只适用于整数。

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

/(division)和//(floor division)操作符产生它们的参数的商。数值参数首先转换为公共类型。整数的除法产生一个浮点数,而整数的底除法得到一个整数;结果是应用于结果的'floor'函数的数学除法。除以零引发ZeroDivisionError异常。

(modulo)操作符产生第一个参数除以第二个参数的余数。数值参数首先转换为公共类型。右参数为零引发ZeroDivisionError异常。参数可以是浮点数,例如3.14%0.7等于0.34(因为3.14等于4*0.7 + 0.34。)模操作符号总是产生与其第二操作数(或零)具有相同符号的结果;结果的绝对值严格小于第二操作数[1]的绝对值。

Floor division和取模操作由下面的等式相关联:x == (x//y)*y + (x%y)Floor division和取模还由内建函数divmod()相关联:divmod(x, y) == (x//y, x%y)[2]

除了执行整数的取模操作,%操作符还被字符串对象重载用于执行字符串的格式化(也叫做插值)。字符串格式化的语法在Python库参考的printf风格的字符串格式化一节讲述。

没有为复数定义floor division操作符,模操作符和divmod()函数。相反,如果合适,应使用abs()函数转换为浮点数。

+(加法)操作符产生其参数的和。其参数必须都是数值或者都是相同类型的序列。在前一种情况下,数字被转换为公共类型,然后加在一起。在后一种情况下,序列被连接。

-(减法)操作符产生其参数差。数值参数首先转换为公共类型。

6.8. 移位操作

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

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

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

n位右移位定义为由pow(2, n)进行的floor除法。n位左移被定义为与pow(2, n)相乘。

注意

在当前实现中,要去右操作数最大为sys.maxsize如果右侧操作数大于sys.maxsize,则会引发OverflowError异常。

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

&操作符产生按位与,它的参数必须是普通整数。

^操作符产生其参数的按位异或(异或),其必须是整数。

|操作符产生其参数的按位(包括)OR,它必须是整数。

6.10. 比较操作

与C不同,Python中的所有比较操作具有相同的优先级,低于任何算术,移位或逐位操作的优先级。与C不同的还有,类似a < b < c这样的表达式就是数学中传统的含义。

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并不意味着ac 之间会有比较,所以x < y > z 是完全合法的(尽管不漂亮)。

6.10.1. 值比较

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

对象,值和类型一章说明对象具有值(除了类型和ID外)。对象的值在Python中是一个相当抽象的概念:例如,对象的值没有规范的访问方法。此外,不要求对象的值应以特定方式构造,例如包括其所有的数据属性。比较操作符实现了对象的值是什么的特定概念。可以认为它们通过它们的比较实现间接地定义对象的值。

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

等式比较的默认行为(==!=)基于对象的ID。因此,具有相同ID的实例的等式比较结果相等,并且具有不同ID的实例的等式比较结果不等。这种默认行为的动机是所有对象应该是自反的(即,x is y表示x == y)。

不提供默认的顺序比较(<, >, <=, and >=);尝试这样做将引发TypeError这种默认行为的动机是缺乏与等式相似的不变量。

默认等式比较的行为,即具有不同ID的实例总是不相等,可能与需要具有对象值和基于值的等式的合理定义的类型相反。这种类型需要定制它们的比较行为,事实上,一些内置类型已经做到了。

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

  • 内置数值类型(数值类型 - int,float,complex)和标准库类型fraction.Fractiondecimal.Decimal的数值可以在其类型之内和之间进行比较,同时限制复数不支持顺序比较。在所涉及的类型的限制内,它们在数学上(算法上)进行比较而不损失精度。

    非数字值float('NaN')Decimal('NaN')比较特殊。它们与自己完全相同(x is x为真),但是与它们自己不相等(x == x为假)。此外,将任何数字与非数字值比较将返回False例如,3 < float('NaN')float('NaN') < 3都将返回False

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

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

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

  • 序列(tuple列表range的实例)只能在每个类型中比较,但ranges不支持顺序比较。这些类型之间的等式比较结果不等,并且在这些类型之间进行排序比较引发TypeError

    序列使用对应元素的比较来按照字典顺序进行比较,其中强制执行元素的自反性。

    在强制元素的自反性中,容器的比较假设对于容器的元素xx == x始终为真。基于该假设,首先比较元素身份,并且仅对不同元素执行元素比较。如果比较的元素是反射性的,则该方法产生与严格元素比较相同的结果。对于非自反元素,结果不同于严格元素比较,并且可能令人惊讶:例如,非自反的not-a-number值在列表中使用时导致以下比较行为:

    >>> nan = float('NaN')
    >>> nan is nan
    True
    >>> nan == nan
    False                 <-- the defined non-reflexive behavior of NaN
    >>> [nan] == [nan]
    True                  <-- list enforces reflexivity and tests identity first
    

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

    • 对于两个容器比较相等,它们必须具有相同的类型,具有相同的长度,并且每对相应的元素必须比较相等(例如,[1,2] == (1,2)为假,因为类型不同)。
    • 支持顺序比较的容器与它们的第一个不相等元素排序相同(例如[1,2,x] <= [1,2,y]x <= y的值相同)。如果相应的元素不存在,则较短的容器在前面(例如,[1,2] < [1,2,3]为真)。
  • 映射(dict的实例)当且仅当它们具有相等的(key, value)对时才相等。键和元素的相等性比较强制自反性。

    顺序比较(<, >, <=, and >=)引发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 y > z表示x > z

    x < y y <= z 表示 x < z

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

    x == y and not x != y

    x < y and not x >= y (for total ordering)

    x > y and not x <= y (for total ordering)

    最后两个表达式适用于完全有序的容器。到序列,而不是集合或映射)。另请参见total_ordering()装饰器。

Python不强制执行这些一致性规则。事实上,not-a-number值就是没有遵循这些规则的一个例子。

6.10.2. 成员资格测试操作

操作符innot in用于测试成员资格。如果xs的一个成员那么x in s为真,否则为假。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为真当且仅当xy的子串。等效测试是y.find(x) != -1空字符串始终被视为任何其他字符串的子字符串,因此"" in "abc"返回True

对于定义__contains__方法的用户定义类,x in y为真,当且仅当y.__contains__(x)为真。

对于未定义__contains__(),但是定义__iter__()的用户定义类,x in y为真,如果在迭代y时,存在某个值z使得x == z如果在迭代期间引发异常,就像in引发该异常一样。

最后,尝试旧式迭代协议:如果类定义__getitem__()x in y为真,当且仅当存在非负整数索引i使得x == y[i],并且所有小于它的整数索引不引发IndexError异常。(如果出现任何其他异常,就像in引发该异常一样)。

not in操作符定义为具有与in相反的值。

6.10.3. 身份比较

操作符isis not测试对象ID:x is y为真,当且仅当xy是相同的对象。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

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

如果其参数为假,则操作符not产生True,否则为False

表达式x and y首先计算x;如果x为假,则返回其值;否则,将计算y并返回生成的值。

表达式x or y首先计算x;如果x为真,则返回其值;否则,将计算y并返回生成的值。

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

6.12. 条件表达式

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

条件表达式(有时称为“ternary操作符”)具有所有Python操作的最低优先级。

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

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

6.13. Lambdas

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

Lambda表达式(有时称为lambda形式)用于创建匿名函数。表达式lambda arguments: expression生成一个函数对象。未命名的对象的行为像一个函数对象,定义如下:

def <lambda>(arguments):
    return expression

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

6.14. 表达式列表

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

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

星号*表示可迭代的分拆其操作数必须是一个可迭代对象该可迭代对象扩展为一系列的元素项,这些元素包括在分拆所在位置的新的元组,列表或集合中。

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

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

6.15. 计算的顺序

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.16. 操作符优先级

下表总结了Python中的操作符号优先级,从最低优先级(最低绑定)到最高优先级(最高绑定)。操作符在同一个框有相同的优先级。除非明确给出语法,否则操作符是二进制的。在同一格中的操作符从左到右分组(除了幂操作,从右到左分组)。

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

操作符描述
lambdaλ表达式
if - else条件表达式
or布尔OR
and布尔AND
not x布尔NOT
in, not in, is, is not, <, <=, >, >=, !=, ==比较,包括成员资格测试和身份测试
|按位或
^按位异或
&按位AND
<<, >>转变
+-加法和减法
*@///%乘法,矩阵乘法除法,余数[5]
+x-x~x正,负,按位NOT
**指数[6]
await xAwait表达
x[index]x[index:index]x(arguments...)x.attribute下标,切片,调用,属性引用
(expressions...)[expressions...]{key: value ...} / t6>{expressions...}绑定或元组表示式、列表表示式、字典表示式、集合表示式

脚注

[1]虽然abs(x%y) < abs(y)在数学上为真,但是由于数字的舍入它对于浮点数可能不为真。例如,假设在一个平台上有一个Python浮点数是IEEE 754双精度数字,为了让-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 % yx非常接近。
[3]

Unicode标准区分码点(例如,U+0041)和抽象字符(例如“LATIN CAPITAL LETTER A”)。虽然Unicode中的大多数抽象字符仅使用一个代码点来表示,但是还有很多抽象字符可以另外使用多于一个代码点的序列来表示。例如,抽象字符“LATIN CAPITAL LETTER C WITH CEDILLA”可以表示为在代码位置U + 00C7处的单个预组成字符,或者作为基本字符 t1的序列>在编码位置U + 0043(拉丁资本字母C),随后是在编码位置U + 0327(组合CEDILLA)处的组合字符

字符串上的比较操作符号在Unicode码点级别进行比较。这可能对人类是不直观的。例如,"\u00C7" == "\u0043\u0327"False,尽管两个字符串表示相同的抽象字符“LATIN CAPITAL LETTER C WITH CEDILLA”。

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

[4]由于自动垃圾回收,自由列表和描述符的动态特性,你可能会注意到is操作符在某些用途中看起来不寻常的行为,如涉及实例方法或常量之间的比较。查看他们的文档了解更多信息。
[5]操作符也用于字符串格式化;适用相同的优先级。
[6]幂操作符**比其右侧的一元算术或位操作符号绑定性弱,即2**-10.5