5. Expressions

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

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

name ::=  othername

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

5.1. 算术转换

当下面对算术运算符的描述使用短语“将数字参数转换为通用类型”时,将使用列在Coercion rules的强制规则来对这些参数进行强制。如果两个参数都是标准数值类型,则将应用以下强制:

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

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

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

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

一些其他规则适用于某些运算符(例如,'%'运算符的字符串左参数)。扩展可以定义自己的强制。

5.2. Atoms

原子是表达式的最基本元素。最简单的原子是标识符或 Literals。用反引号或括号,方括号或大括号括起来的形式在语法上也归类为原子。原子的语法为:

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

5.2.1. 标识符(名称)

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

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

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

5.2.2. Literals

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

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

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

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

5.2.3. 带括号的形式

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

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

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

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

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

5.2.4. Lists 显示

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

list_display        ::=  "[" [expression_list | list_comprehension] "]"
list_comprehension  ::=  expression list_for
list_for            ::=  "for" target_list "in" old_expression_list [list_iter]
old_expression_list ::=  old_expression [("," old_expression)+ [","]]
old_expression      ::=  or_test | old_lambda_expr
list_iter           ::=  list_for | list_if
list_if             ::=  "if" old_expression [list_iter]

列表显示将产生一个新的列表对象。pass提供表达式列表或列表理解来指定其内容。提供逗号分隔的表达式列表时,将按从左到右的 Sequences 评估其元素并将其按 Sequences 放入列表对象中。提供列表推导时,它包含一个表达式,后跟至少一个for子句和零个或多个forif子句。在这种情况下,新列表的元素是pass将每个forif子句视为一个块,从左到右嵌套,并在每次到达最内层的块时评估表达式以产生一个列表元素而生成的元素[1]

5.2.5. 显示集合和词典

为了构造集合或字典,Python 提供了称为“显示”的特殊语法,每种语法有两种形式:

  • 容器内容已明确列出,或

  • 它们是pass一组称为* comprehension *的循环和过滤指令来计算的。

理解的常见语法元素是:

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子句和零个或多个forif子句。在这种情况下,新容器的元素是pass将每个forif子句视为一个块,从左到右嵌套,并在每次到达最里面的块时评估表达式以产生一个元素而生成的元素。

请注意,理解是在单独的作用域中执行的,因此在目标列表中分配的名称不会在包含的作用域中“泄漏”。

5.2.6. 生成器表达式

生成器表达式是用括号括起来的紧凑型生成器符号:

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

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

当为生成器对象调用__next__()方法时(与普通生成器相同的方式),对生成器表达式中使用的变量进行延迟计算。但是,最左边的for子句会立即被求值,以便在处理生成器表达式的代码中,可以先看到由它产生的错误。由于后面的for子句可能取决于先前的for循环,因此无法立即对其进行评估。例如:(x*y for x in range(10) for y in bar(x))

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

5.2.7. 字典显示

词典显示是用大括号括起来的一系列可能是空的键/基准对:

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

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

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

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

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

5.2.8. 设置显示

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

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

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

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

5.2.9. 字符串转换

字符串转换是一个用反引号(也称为反引号)括起来的表达式列表:

string_conversion ::=  "`" expression_list "`"

字符串转换将评估包含的表达式列表,并根据特定于其类型的规则将结果对象转换为字符串。

如果对象是仅包含类型之一的对象的字符串,数字None或 Tuples,列表或字典,则生成的字符串是有效的 Python 表达式,可以将其传递给内置函数eval()以产生具有相同值的表达式(如果包含浮点数,则为近似值)。

(特别是,转换字符串时,会在字符串周围加上引号,并将“有趣”字符转换为可以安全打印的转义序列.)

递归对象(例如,直接或间接包含对自己的引用的列表或字典)使用...表示递归引用,并且结果无法传递给eval()以获得相等的值(取而代之的是SyntaxError)。

内置函数repr()的参数转换与括号和反引号中的转换完全相同。内置函数str()执行类似但更用户友好的转换。

5.2.10. yield表达

yield_atom       ::=  "(" yield_expression ")"
yield_expression ::=  "yield" [expression_list]

2.5 版的新Function。

yield表达式仅在定义生成器函数时使用,并且只能在函数定义的主体中使用。在函数定义中使用yield表达式足以使该定义创建一个生成器函数,而不是普通函数。

调用生成器函数时,它将返回称为生成器的迭代器。然后,该生成器控制生成器Function的执行。当调用生成器的方法之一时,执行开始。那时,执行 continue 进行到第一个yield表达式,在此它再次被挂起,将expression_list的值返回给生成器的调用者。暂停是指保留所有局部状态,包括局部变量的当前绑定,指令指针和内部评估堆栈。当pass调用生成器的方法之一恢复执行时,该函数可以像yield表达式只是另一个外部调用一样 continue 进行。恢复后yield表达式的值取决于恢复执行的方法。

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

5.2.10.1. 生成器-迭代器方法

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

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

  • generator. next ( )

    • 开始执行生成器函数或在最后执行的yield表达式处 continue 执行。当使用next()方法恢复生成器函数时,当前yield表达式始终求值为None。然后执行 continue 到下一个yield表达式,在此处再次暂停生成器,并将expression_list的值返回给next()的调用方。如果生成器退出而未产生其他值,则会引发StopIteration异常。
  • generator. send(* value *)

    • 恢复执行并将值“发送”到生成器函数。 value参数成为当前yield表达式的结果。 send()方法返回生成器产生的下一个值,如果生成器退出而未产生另一个值,则返回StopIteration。调用send()以启动生成器时,必须以None作为参数来调用它,因为没有yield表达式可以接收该值。
  • generator. throw(* type * [,* value * [,* traceback *]])

    • 在生成器暂停的位置引发type类型的异常,并返回生成器函数产生的下一个值。如果生成器退出而未产生其他值,则会引发StopIteration异常。如果生成器函数未捕获传入的异常,或引发了另一个异常,则该异常将传播到调用方。
  • generator. close ( )
    • 在生成器Function暂停的点处引发GeneratorExit。如果然后生成器函数引发StopIteration(pass正常退出或由于已被关闭)或GeneratorExit(由于未捕获异常),则 close 返回其调用方。如果生成器产生一个值,则会引发RuntimeError。如果生成器引发任何其他异常,则将其传播到调用方。如果生成器由于异常或正常退出而已退出,则close()不执行任何操作。

这是一个简单的示例,演示了生成器和生成器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, e:
...                 value = e
...     finally:
...         print "Don't forget to clean up when 'close()' is called."
...
>>> generator = echo(1)
>>> print generator.next()
Execution starts when 'next()' is called for the first time.
1
>>> print generator.next()
None
>>> print generator.send(2)
2
>>> generator.throw(TypeError, "spam")
TypeError('spam',)
>>> generator.close()
Don't forget to clean up when 'close()' is called.

See also

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

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

5.3. Primaries

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

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

5.3.1. 属性参考

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

attributeref ::=  primary "." identifier

主要对象必须评估为支持属性引用的类型的对象,例如模块,列表或实例。然后要求该对象产生名称为标识符的属性。如果此属性不可用,则会引发异常AttributeError。否则,所生成对象的类型和值由对象确定。对同一属性引用的多次评估可能会产生不同的对象。

5.3.2. Subscriptions

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

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

主要对象必须求值为序列或 Map 类型的对象。

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

如果主变量是序列,则表达式列表必须求值为纯整数。如果此值为负数,则将序列的长度加到该值上(例如x[-1]选择x的最后一项.)结果值必须是一个非负整数,该整数必须小于序列中的项数,并且 subscription 选择其索引为该值(从零开始计数)的项目。

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

5.3.3. Slicings

切片选择序列对象中的一系列项目(例如,字符串,Tuples 或列表)。切片可用作赋值或del语句中的表达式或目标。切片的语法:

slicing          ::=  simple_slicing | extended_slicing
simple_slicing   ::=  primary "[" short_slice "]"
extended_slicing ::=  primary "[" slice_list "]"
slice_list       ::=  slice_item ("," slice_item)* [","]
slice_item       ::=  expression | proper_slice | ellipsis
proper_slice     ::=  short_slice | long_slice
short_slice      ::=  [lower_bound] ":" [upper_bound]
long_slice       ::=  short_slice ":" [stride]
lower_bound      ::=  expression
upper_bound      ::=  expression
stride           ::=  expression
ellipsis         ::=  "..."

此处的正式语法有歧义:看起来像表达式列表的任何内容也看起来像切片列表,因此任何订阅都可以解释为切片。pass定义在这种情况下,作为预订的解释优先于作为切片的解释,可以消除歧义,而不是进一步使语法复杂化(如果切片列表不包含适当的切片或椭圆,则是这种情况)。同样,当切片列表中只有一个短切片且没有尾随逗号时,解释为简单切片优先于扩展切片。

简单切片的语义如下。主要对象必须求值为序列对象。下界和上限表达式(如果存在)必须计算为纯整数;默认值分别为零和sys.maxint。如果任一边界为负,则将序列的长度添加到其中。现在,切片将选择索引为* k 的所有项目,使得i <= k < j其中 i j 为指定的上下限。这可能是一个空序列。如果 i j *不在有效索引范围内(这两项不存在,因此不会被选择),这不是错误。

扩展切片的语义如下。主对象必须求值到 Map 对象,并使用从切片列表构造的键对其进行索引,如下所示。如果切片列表包含至少一个逗号,则关键字为一个包含切片项目转换的 Tuples;否则为 false。否则,单独切片项的转换是关键。作为表达式的切片项的转换就是该表达式。Ellipsis号切片项目的转换是内置的Ellipsis对象。适当切片的转换是切片对象(请参见标准类型层次结构),其startstopstep属性是分别以下限,上限和 Span 给出的表达式的值,用None代替缺失的表达式。

5.3.4. Calls

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

call                 ::=  primary "(" [argument_list [","]
                          | expression genexpr_for] ")"
argument_list        ::=  positional_arguments ["," keyword_arguments]
                            ["," "*" expression] ["," keyword_arguments]
                            ["," "**" expression]
                          | keyword_arguments ["," "*" expression]
                            ["," "**" expression]
                          | "*" expression ["," keyword_arguments] ["," "**" expression]
                          | "**" expression
positional_arguments ::=  expression ("," expression)*
keyword_arguments    ::=  keyword_item ("," keyword_item)*
keyword_item         ::=  identifier "=" expression

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

主要对象必须评估为可调用对象(用户定义的函数,内置函数,内置对象的方法,类对象,类实例的方法以及某些类实例本身是可调用的;扩展可以定义其他可调用对象类型) 。在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必须计算为可迭代。来自此迭代器的元素被视为附加的位置参数。如果存在位置参数* x1 ,…, xN ,并且expression求值为序列 y1 ,…, yM ,则等效于使用 MN 位置参数 x1 ,…, xN 的调用, y1 ,…, yM *。

这样的结果是,尽管*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必须求出一个 Map,该 Map 的内容被视为其他关键字参数。如果关键字同时出现在expression中并且作为显式关键字参数出现,则会引发TypeError异常。

使用语法*identifier**identifier的形式参数不能用作位置参数插槽或关键字参数名称。使用语法(sublist)的形式参数不能用作关键字参数名称;最外面的子列表对应于一个未命名的参数槽,并且在完成所有其他参数处理之后,使用通常的 Tuples 分配规则将参数值分配给子列表。

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

如果是-

  • 用户定义的函数:

    • 执行该函数的代码块,并向其传递参数列表。代码块要做的第一件事是将形式参数绑定到参数。 Function definitions部分对此进行了说明。当代码块执行return语句时,这将指定函数调用的返回值。
  • 内置函数或方法:

    • 结果由 Interpreter 决定;有关内置函数和方法的说明,请参见Built-in Functions
  • 一个类对象:

    • 返回该类的新实例。
  • 类实例方法:

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

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

5.4. power operator

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

power ::=  primary ["**" u_expr]

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

当使用两个参数调用时,幂运算符的语义与内置pow()函数的语义相同:它产生的左参数升为右参数的幂。首先将数字参数转换为通用类型。结果类型是强制后的参数类型。

对于混合操作数类型,适用于二进制算术运算符的强制规则。对于 int 和 long int 操作数,除非第二个参数为负,否则结果的类型与操作数相同(强制后)。在这种情况下,所有参数都将转换为 float 并传递 float 结果。例如,10**2返回100,但是10**-2返回0.01。 (这最后一个Function是在 Python 2.2 中添加的.在 Python 2.1 及之前的版本中,如果两个参数均为整数类型,而第二个参数为负,则会引发异常)。

0.0提升至负功率会导致ZeroDivisionError。将负数提高到小数幂会导致ValueError

5.5. 一元算术和按位运算

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

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

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

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

一元~(取反)运算符产生其纯或长整数参数的按位取反。 x的按位求反定义为-(x+1)。它仅适用于整数。

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

5.6. 二进制算术运算

二进制算术运算具有常规的优先级。请注意,其中一些操作也适用于某些非数字类型。除幂运算符外,只有两个级别,一个用于乘法运算符,一个用于加法运算符:

m_expr ::=  u_expr | m_expr "*" u_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

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

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

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

整数除法和模运算符pass以下标识连接:x == (x/y)*y + (x%y)。整数除法和模也与内置函数divmod()divmod(x, y) == (x/y, x%y)连接。这些身份不适用于浮点数。在用floor(x/y)floor(x/y) - 1 [3]代替x/y的地方大约有类似的标识。

除了对数字执行模运算之外,%运算符还被字符串和 unicode 对象超载以执行字符串格式化(也称为插值)。字符串格式的语法在 Python 库参考字符串格式化操作部分中进行了描述。

从版本 2.3 开始弃用:不再为复数定义地板除法运算符,模运算符和divmod()函数。相反,如果合适,请使用abs()函数转换为浮点数。

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

-(减)运算符产生其自变量的差。首先将数字参数转换为通用类型。

5.7. 换档操作

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

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

这些运算符接受纯或长整数作为参数。参数将转换为通用类型。他们将第一个参数向左或向右移动第二个参数给定的位数。

向右移位* n 位定义为被pow(2, n)除。向左移位 n *位定义为与pow(2, n)相乘。负班次计数引发ValueError异常。

Note

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

5.8. 二进制按位运算

这三个按位运算中的每一个都有不同的优先级:

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(异或),该值必须为纯整数或长整数。参数将转换为通用类型。

|运算符产生其参数的按位(含)或,其必须为纯整数或长整数。参数将转换为通用类型。

5.9. 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是完全合法的(尽管可能不是很漂亮)。

形式<>!=是等效的;为了与 C 保持一致,最好使用!=;在下面提到!=的地方也接受<><>拼写被认为是过时的。

5.9.1. 价值比较

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

对象,值和类型章语句对象具有值(除了类型和标识之外)。在 Python 中,对象的值是一个相当抽象的概念:例如,对于对象的值,没有规范的访问方法。而且,不要求对象的值应该以特定的方式构造,例如。由其所有数据属性组成。比较运算符实现了关于对象值是什么的特定概念。可以pass比较实现将它们视为间接定义对象的值。

类型可以pass实现cmp()方法或Basic customization中描述的lt()之类的“丰富比较方法” *来自定义其比较行为。

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

默认 Sequences 比较(<><=>=)给出了一致但任意的 Sequences。

(这种不寻常的比较定义用于简化排序和innot in运算符之类的操作的定义。将来,不同类型对象的比较规则可能会更改。)

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

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

  • 内置数字类型(数值类型-整型,浮点型,长整型)的数量以及标准库类型fractions.Fractiondecimal.Decimal的数量可以在其类型之内和之间进行比较,但有一个限制,即复数不支持 Sequences 比较。在涉及的类型的范围内,它们在 math 上(算法上)比较正确而不会损失精度。

  • 字符串(strunicode的实例)使用其字符的数字等效项(内置函数ord()的结果)在字典上进行比较。 [4]比较 8 位字符串和 Unicode 字符串时,会将 8 位字符串转换为 Unicode。如果转换失败,则认为字符串不相等。

  • tuplelist的实例只能在每种类型中进行比较。这些类型之间的相等比较会导致不相等,并且这些类型之间的排序比较会给出任意 Sequences。

这些序列在字典上使用相应元素的比较进行比较,从而增强了元素的反射性。

在加强元素的自反性时,集合的比较假定对于集合元素xx == x始终为 true。基于该假设,首先比较元素标识,然后仅对不同元素执行元素比较。如果比较的元素是自反的,则此方法产生与严格元素比较相同的结果。对于非自反元素,结果与严格元素比较的结果不同。

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

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

    • 集合的排序与其第一个不相等元素相同(例如cmp([1,2,x], [1,2,y])返回与cmp(x,y)相同)。如果不存在对应的元素,则将对较短的集合进行排序(例如[1,2] < [1,2,3]为 true)。
  • 当且仅当 Map 具有相等(键,值)对时,Map(实例dict)比较相等。键和值的相等比较可增强自反性。

除平等外,其他结果均得到一致解决,但未另行定义。 [5]

  • 大多数内置类型的其他对象比较不相等,除非它们是同Pair象。在一个程序的执行过程中,可以任意选择,但始终如一地选择是将一个对象视为小于还是大于一个对象。

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

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

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()装饰器。

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

Python 不执行这些一致性规则。

5.9.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上进行迭代时生成了带有x == z的某个值z,则x in yTrue。如果在迭代过程中引发了异常,则in引发了该异常。

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

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

5.9.3. 身份比较

运算符isis not测试对象身份:x is y当且仅当* x y *是同Pair象时为 true。 x is not y产生反真值。 [6]

5.10. 布尔运算

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。 (有关更改方法,请参见nonzero()特殊方法。)

如果其参数为假,则运算符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而不是''。)

5.11. 条件表达式

2.5 版的新Function。

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

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

表达式x if C else y首先计算条件* C ( not * * x );如果 C 为 true,则评估 x 并返回其值;否则,将评估 y *并返回其值。

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

5.12. Lambdas

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

Lambda 表达式(有时称为 lambda 形式)与表达式具有相同的句法位置。它们是创建匿名函数的简写。表达式lambda parameters: expression产生一个函数对象。未命名对象的行为类似于使用定义的函数对象

def <lambda>(parameters):
    return expression

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

5.13. 表达式列表

expression_list ::=  expression ( "," expression )* [","]

一个包含至少一个逗号的表达式列表会产生一个 Tuples。Tuples 的长度是列表中表达式的数量。表达式从左到右计算。

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

5.14. 评估 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

5.15. 运算符优先级

下表总结了 Python 中的运算符优先级,从最低优先级(最低绑定)到最高优先级(大多数绑定)。同一框中的运算符具有相同的优先级。除非明确给出语法,否则运算符为二进制。同一框中的运算符从左到右分组(比较(包括测试)的优先级和优先级从左到右相同(请参阅Comparisons)和取幂,从右到左分组)。

OperatorDescription
lambdaLambda expression
ifelseConditional expression
orBoolean OR
andBoolean AND
not xBoolean NOT
in, not in, is, is not, < , <= , > , >= , <> , != , ==比较,包括成员资格测试和身份测试
|Bitwise OR
^Bitwise XOR
&Bitwise AND
<< , >>Shifts
+ , -加减
* , / , // , %乘法,除法,余数[7]
+x , -x , ~x正,负,按位非
**Exponentiation [8]
x[index] , x[index:index] , x(arguments...) , x.attribute订阅,切片,调用,属性参考
(expressions...) , [expressions...] , {key: value...} , expressions...绑定或 Tuples 显示,列表显示,字典显示,字符串转换

Footnotes

  • [1]

    • 在 Python 2.3 和更高版本中,列表理解将其包含的每个for的控制变量“泄漏”到包含作用域中。但是,此行为已被弃用,并且依赖于它在 Python 3 中将不起作用。
  • [2]

    • 尽管abs(x%y) < abs(y)在 math 上是正确的,但对于浮点数,由于四舍五入,它在数值上可能不是正确的。例如,假设 Python 浮点数在其上的平台是 IEEE 754 双精度数,则为了使-1e-100 % 1e1001e100具有相同的符号,计算结果为-1e-100 + 1e100,该数字在数值上完全等于1e100。函数math.fmod()返回的结果的符号与第一个参数的符号相反,因此在这种情况下返回-1e-100。哪种方法更合适取决于应用程序。
  • [3]

    • 如果 x 非常接近 y 的精确整数倍,则由于舍入,floor(x/y)可能比(x-x%y)/y大 1.在这种情况下,Python 返回后一个结果,以保持divmod(x,y)[0] * y + x % y非常接近x
  • [4]

    • Unicode 标准区分代码点(例如 U 0041)和抽象字符(例如“拉丁文大写字母 A”)。虽然 Unicode 中的大多数抽象字符仅使用一个代码点表示,但是许多抽象字符还可以使用多个代码点的序列表示。例如,抽象字符“带有 CEDILLA 的拉丁文大写字母 C”可以表示为在代码位置 U 00C7 处的单个预组合字符,也可以表示为代码位置 U 0043 上的基本字符的序列(拉丁文大写字母 C ),然后在代码位置 U 0327(COMBINING CEDILLA)后面加上*“ combining character”。

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

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

  • [5]

    • 早期版本的 Python 使用排序(键,值)列表的字典比较,但这对于比较相等的常见情况而言非常昂贵。 Python 的更早版本仅按身份比较字典,但这引起了意外,因为人们希望pass将字典与{}进行比较来测试字典是否为空。
  • [6]

    • 由于自动进行垃圾回收,释放列表以及 Descriptors 的动态性质,您可能会注意到is运算符的某些用法中的异常行为,例如涉及实例方法或常量之间的比较的行为。查看他们的文档以获取更多信息。
  • [7]

    • %运算符还用于字符串格式化;相同的优先级适用。
  • [8]

    • 幂运算符**的绑定不如其右侧的算术或按位一元运算符紧,即2**-10.5