5. 表达式 — Python 文档
5. 表达式
本章解释了 Python 中表达式元素的含义。
语法注释: 在本章和后面的章节中,将使用扩展的 BNF 符号来描述语法,而不是词法分析。 当(一种替代)语法规则具有以下形式时
name ::= othername
并且没有给出语义,这种形式的 name
的语义与 othername
的语义相同。
5.1. 算术转换
当下面对算术运算符的描述使用短语“将数字参数转换为通用类型”时,参数将使用 强制规则 中列出的强制规则进行强制。 如果两个参数都是标准数字类型,则应用以下强制转换:
- 如果任一参数是复数,则另一个将转换为复数;
- 否则,如果任一参数是浮点数,则另一个将转换为浮点数;
- 否则,如果任一参数是长整数,则另一个将转换为长整数;
- 否则,两者都必须是普通整数,不需要转换。
某些附加规则适用于某些运算符(例如,'%' 运算符的字符串左参数)。 扩展可以定义自己的强制。
5.2. 原子
原子是表达式最基本的元素。 最简单的原子是标识符或文字。 用反引号或圆括号、方括号或大括号括起来的形式在语法上也被归类为原子。 原子的语法是:
atom ::= identifier | literal | enclosure enclosure ::= parenth_form | list_display | generator_expression | dict_display | set_display | string_conversion | yield_atom
5.2.1. 标识符(名称)
作为原子出现的标识符是名称。 有关词法定义,请参阅 标识符和关键字 部分,有关命名和绑定的文档,请参阅 命名和绑定 部分。
当名称绑定到一个对象时,对原子的评估会产生该对象。 当名称未绑定时,尝试对其求值会引发 NameError
异常。
私有名称修改:当在类定义中以文本形式出现的标识符以两个或多个下划线字符开头且不以两个或多个下划线结尾时,它被视为 的私有名称 那个班。 在为它们生成代码之前,私有名称被转换为更长的形式。 转换插入类名,在名称前面删除前导下划线并插入一个下划线。 例如,出现在名为 Ham
的类中的标识符 __spam
将被转换为 _Ham__spam
。 此转换与使用标识符的语法上下文无关。 如果转换后的名称非常长(超过 255 个字符),则可能会发生实现定义的截断。 如果类名仅由下划线组成,则不进行任何转换。
5.2.2. 文字
Python 支持字符串文字和各种数字文字:
literal ::= stringliteral | integer | longinteger | floatnumber | imagnumber
对文字的求值产生具有给定值的给定类型(字符串、整数、长整数、浮点数、复数)的对象。 在浮点和虚(复数)文字的情况下,该值可能是近似值。 有关详细信息,请参阅 Literals 部分。
所有文字都对应于不可变数据类型,因此对象的身份不如其值重要。 对具有相同值(在程序文本中相同出现或不同出现)的文字进行多次计算可能会获得相同的对象或具有相同值的不同对象。
5.2.3. 括号形式
带括号的形式是括在括号中的可选表达式列表:
parenth_form ::= "(" [expression_list] ")"
带括号的表达式列表产生表达式列表产生的任何结果:如果列表包含至少一个逗号,则产生一个元组; 否则,它产生构成表达式列表的单个表达式。
一对空括号产生一个空元组对象。 由于元组是不可变的,文字规则适用(即,空元组的两次出现可能会或可能不会产生相同的对象)。
请注意,元组不是由括号形成的,而是使用逗号运算符形成的。 空元组是个例外,需要括号 是 —— 允许表达式中没有括号的“无”会导致歧义并允许常见的拼写错误未被发现。
5.2.4. 列表显示
列表显示是括在方括号中的一系列可能为空的表达式:
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]
列表显示产生一个新的列表对象。 它的内容是通过提供表达式列表或列表理解来指定的。 当提供逗号分隔的表达式列表时,它的元素从左到右求值并按该顺序放入列表对象中。 提供列表推导式时,它由一个表达式组成,后跟至少一个 for 子句和零个或多个 for 或 if 子句。 在这种情况下,新列表的元素是通过考虑每个 for 或 if 子句一个块,从左到右嵌套,并评估表达式而产生的元素每次到达最里面的块时生成一个列表元素 1。
5.2.5. 集合和字典的显示
为了构建集合或字典,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 子句块,从左到右嵌套,并评估表达式而产生的元素每次到达最里面的块时生成一个元素。
请注意,理解是在单独的作用域中执行的,因此分配给目标列表中的名称不会“泄漏”在封闭的作用域中。
5.2.6. 生成器表达式
生成器表达式是括号中的紧凑生成器符号:
generator_expression ::= "(" expression comp_for ")"
生成器表达式产生一个新的生成器对象。 它的语法与推导式相同,只是它用括号括起来,而不是用方括号或花括号括起来。
当为生成器对象调用 __next__()
方法时,生成器表达式中使用的变量会被延迟计算(与普通生成器的方式相同)。 然而,最左边的 for 子句会被立即计算,因此它产生的错误可以在处理生成器表达式的代码中的任何其他可能的错误之前被看到。 后续的 for 子句无法立即求值,因为它们可能依赖于之前的 for 循环。 例如:(x*y for x in range(10) for y in bar(x))
。
在只有一个参数的调用中可以省略括号。 有关详细信息,请参阅 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
字典显示产生一个新的字典对象。
如果给出了以逗号分隔的键/数据对序列,则从左到右计算它们以定义字典的条目:每个键对象用作字典中的键以存储相应的数据。 这意味着您可以在键/数据列表中多次指定相同的键,并且该键的最终字典值将是最后一个给出的值。
与列表推导式和集合推导式相比,字典推导式需要两个用冒号分隔的表达式,后跟通常的“for”和“if”子句。 当理解运行时,生成的键和值元素按照它们的生成顺序插入到新字典中。
键值类型的限制在前面的 标准类型层次结构 部分中列出。 (总而言之,键类型应该是 hashable,它排除了所有可变对象。)不会检测到重复键之间的冲突; 为给定键值存储的最后一个数据(显示中最右侧的文本)优先。
5.2.8. 设置显示
集合显示由大括号表示,并通过缺少分隔键和值的冒号与字典显示区分开来:
set_display ::= "{" (expression_list | comprehension) "}"
集合显示产生一个新的可变集合对象,其内容由表达式序列或推导式指定。 当提供一个逗号分隔的表达式列表时,它的元素从左到右求值并添加到集合对象中。 当提供了一个推导式时,集合是由推导式产生的元素构造的。
不能用 {}
构造空集; 这个字面量构造了一个空字典。
5.2.9. 字符串转换
字符串转换是一个用反向包围的表达式列表(又名 向后)报价:
string_conversion ::= "`" expression_list "`"
字符串转换评估包含的表达式列表,并根据特定于其类型的规则将结果对象转换为字符串。
如果对象是字符串、数字、None
或仅包含类型为其中之一的对象的元组、列表或字典,则生成的字符串是有效的 Python 表达式,可以传递给内置的在函数 eval() 中生成具有相同值的表达式(或近似值,如果涉及浮点数)。
(特别是,转换字符串会在其周围添加引号,并将“有趣”字符转换为可安全打印的转义序列。)
递归对象(例如直接或间接包含对自身的引用的列表或字典)使用 ...
表示递归引用,并且结果不能传递给 eval() 以得到一个相等的值(SyntaxError
将被提升)。
内置函数 repr() 在其参数中执行与将其括在括号和反引号中完全相同的转换。 内置函数 str() 执行类似但更用户友好的转换。
5.2.10. 产量表达式
yield_atom ::= "(" yield_expression ")" yield_expression ::= "yield" [expression_list]
2.5 版中的新功能。
yield 表达式仅在定义生成器函数时使用,并且只能在函数定义的主体中使用。 在函数定义中使用 yield 表达式足以使该定义创建生成器函数而不是普通函数。
当一个生成器函数被调用时,它返回一个称为生成器的迭代器。 该生成器然后控制生成器函数的执行。 当调用生成器的方法之一时,执行开始。 此时,执行继续执行第一个 yield 表达式,在那里它再次暂停,将 expression_list 的值返回给生成器的调用者。 挂起是指保留所有局部状态,包括局部变量的当前绑定、指令指针和内部计算堆栈。 当通过调用生成器的方法之一恢复执行时,该函数可以完全像 yield 表达式只是另一个外部调用一样继续执行。 恢复后 yield 表达式的值取决于恢复执行的方法。
所有这些使得生成器函数与协程非常相似; 它们多次产生,它们有多个入口点并且它们的执行可以被暂停。 唯一的区别是生成器函数不能控制在它产生之后应该在哪里继续执行; 控制总是转移到生成器的调用者。
5.2.10.1. 生成器迭代器方法
本小节描述生成器迭代器的方法。 它们可用于控制生成器函数的执行。
请注意,当生成器已经在执行时调用下面的任何生成器方法会引发 ValueError
异常。
- generator.next()
- 开始执行生成器函数或在最后执行的 yield 表达式处继续执行。 当使用 next() 方法恢复生成器函数时,当前的 yield 表达式始终计算为 None。 然后继续执行下一个 yield 表达式,在那里生成器再次暂停,并将 expression_list 的值返回给 next() 的调用者. 如果生成器退出而没有产生另一个值,则会引发
StopIteration
异常。
- generator.send(value)
- 恢复执行并将值“发送”到生成器函数中。
value
参数成为当前 yield 表达式的结果。 send() 方法返回生成器生成的下一个值,或者如果生成器退出而没有生成另一个值,则引发StopIteration
。 当调用 send() 启动生成器时,必须以 None 作为参数调用,因为没有 yield 表达式可以接收值.
- generator.throw(type[, value[, traceback]])
- 在生成器暂停时引发类型为
type
的异常,并返回生成器函数产生的下一个值。 如果生成器退出而没有产生另一个值,则会引发StopIteration
异常。 如果生成器函数没有捕获传入的异常,或者引发不同的异常,则该异常会传播给调用者。
- generator.close()
- 在生成器功能暂停的位置引发
GeneratorExit
。 如果生成器函数随后引发StopIteration
(通过正常退出,或由于已经关闭)或GeneratorExit
(通过未捕获异常),则 close 返回到其调用者。 如果生成器产生一个值,则会引发RuntimeError
。 如果生成器引发任何其他异常,则会将其传播给调用者。 close() 如果生成器由于异常或正常退出而已经退出,则不执行任何操作。
这是一个简单的示例,演示了生成器和生成器函数的行为:
>>> 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.
5.3. 初选
原色代表了语言中绑定最紧密的操作。 它们的语法是:
primary ::= atom | attributeref | subscription | slicing | call
5.3.1. 属性引用
属性引用是一个primary,后跟一个句点和一个名称:
attributeref ::= primary "." identifier
主要必须评估为支持属性引用的类型的对象,例如,模块、列表或实例。 然后要求该对象生成名称为标识符的属性。 如果此属性不可用,则会引发异常 AttributeError
。 否则,产生的对象的类型和值由对象决定。 对同一属性引用的多次评估可能会产生不同的对象。
5.3.2. 订阅
订阅选择序列(字符串、元组或列表)或映射(字典)对象的项目:
subscription ::= primary "[" expression_list "]"
主要必须评估为序列或映射类型的对象。
如果主要是映射,则表达式列表必须计算其值为映射键之一的对象,并且订阅选择映射中与该键对应的值。 (表达式列表是一个元组,除非它只有一个项目。)
如果主要是一个序列,则表达式列表必须计算为一个普通整数。 如果此值为负,则将序列的长度添加到其中(例如,x[-1]
选择 x
的最后一项。)结果值必须是小于序列中的项目数,订阅选择索引为该值的项目(从零开始计数)。
字符串的项是字符。 字符不是单独的数据类型,而是一个由一个字符组成的字符串。
5.3.3. 切片
切片选择序列对象中的一系列项目(例如,字符串、元组或列表)。 切片可以用作表达式或赋值或 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 ::= "..."
这里的形式语法有歧义:任何看起来像表达式列表的东西也看起来像切片列表,因此任何订阅都可以解释为切片。 通过定义在这种情况下作为订阅的解释优先于作为切片的解释(如果切片列表不包含适当的切片或省略号就是这种情况),而不是进一步使语法复杂化,从而消除歧义。 类似地,当切片列表只有一个短切片并且没有尾随逗号时,作为简单切片的解释优先于作为扩展切片的解释。
简单切片的语义如下。 主要必须评估为序列对象。 下限和上限表达式(如果存在)必须计算为纯整数; 默认值分别为零和 sys.maxint
。 如果任一边界为负,则将序列的长度添加到其中。 切片现在选择索引为 k 的所有项目,使得 i <= k < j
其中 i 和 j 是指定的下限和上限。 这可能是一个空序列。 如果 i 或 j 位于有效索引范围之外(此类项目不存在,因此未选择它们),这不是错误。
扩展切片的语义如下。 主要必须评估为映射对象,并且它使用从切片列表构造的键进行索引,如下所示。 如果切片列表至少包含一个逗号,则键是一个包含切片项转换的元组; 否则,孤片项的转换是关键。 作为表达式的切片项目的转换就是该表达式。 省略号切片项的转换是内置的 Ellipsis
对象。 正确切片的转换是一个切片对象(参见 标准类型层次结构),其 start
、stop
和 step
属性是分别以下限、上限和步幅给出的表达式,用 None
替换缺失的表达式。
5.3.4. 通话
调用调用可调用对象(例如, 函数 ),其中可能包含一系列空 参数 :
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
位置参数和关键字参数之后可能会出现尾随逗号,但不会影响语义。
主要必须评估为可调用对象(用户定义的函数、内置函数、内置对象的方法、类对象、类实例的方法以及某些类实例本身是可调用的;扩展可以定义其他可调用对象类型) . 在尝试调用之前评估所有参数表达式。 请参阅 函数定义 部分,了解形式 参数 列表的语法。
如果存在关键字参数,它们首先被转换为位置参数,如下所示。 首先,为形参创建未填充槽的列表。 如果有 N 个位置参数,它们被放置在前 N 个槽中。 接下来,对于每个关键字参数,使用标识符来确定对应的槽(如果标识符与第一个形参名称相同,则使用第一个槽,以此类推)。 如果插槽已被填满,则会引发 TypeError
异常。 否则,参数的值被放置在槽中,填充它(即使表达式是 None
,它也填充了槽)。 处理完所有参数后,仍未填充的插槽将使用函数定义中的相应默认值填充。 (默认值在定义函数时计算一次;因此,用作默认值的可变对象(例如列表或字典)将被所有未为相应插槽指定参数值的调用共享;这应该通常应避免。)如果存在任何未指定默认值的未填充插槽,则会引发 TypeError
异常。 否则,填充槽的列表将用作调用的参数列表。
如果位置参数比形式参数槽多,则会引发 TypeError
异常,除非存在使用语法 *identifier
的形式参数; 在这种情况下,该形式参数接收一个包含多余位置参数的元组(如果没有多余的位置参数,则接收一个空元组)。
如果任何关键字参数与形式参数名称不对应,则会引发 TypeError
异常,除非存在使用语法 **identifier
的形式参数; 在这种情况下,该形式参数接收一个包含多余关键字参数的字典(使用关键字作为键,参数值作为相应的值),如果没有多余的关键字参数,则接收一个(新的)空字典。
如果语法 *expression
出现在函数调用中,则 expression
必须计算为一个可迭代对象。 来自这个可迭代对象的元素被视为额外的位置参数; 如果有位置参数 x1, ..., xN, 和 expression
计算结果为序列 y1, ..., yM ],这相当于调用了 M+N 个位置参数 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
必须计算为映射,其内容被视为附加关键字参数。 如果关键字同时出现在 expression
和作为显式关键字参数中,则会引发 TypeError
异常。
使用语法 *identifier
或 **identifier
的形式参数不能用作位置参数槽或关键字参数名称。 使用语法 (sublist)
的形式参数不能用作关键字参数名称; 最外面的子列表对应于单个未命名的参数槽,在所有其他参数处理完成后,使用通常的元组分配规则将参数值分配给子列表。
调用总是返回一些值,可能是 None
,除非它引发异常。 该值的计算方式取决于可调用对象的类型。
如果是-
- 用户定义函数:
- 执行函数的代码块,将参数列表传递给它。 代码块要做的第一件事是将形参绑定到实参; 这在 函数定义 部分进行了描述。 当代码块执行 return 语句时,它指定函数调用的返回值。
- 内置函数或方法:
- 结果取决于解释器; 有关内置函数和方法的说明,请参阅 内置函数 。
- 一个类对象:
- 返回该类的新实例。
- 一个类实例方法:
- 相应的用户定义函数被调用,参数列表比调用的参数列表长一个:实例成为第一个参数。
- 一个类实例:
- 该类必须定义一个
__call__()
方法; 效果与调用该方法的效果相同。
5.4. 电力运营商
幂运算符比左边的一元运算符绑定得更紧密; 它的绑定不如右侧的一元运算符紧密。 语法是:
power ::= primary ["**" u_expr]
因此,在无括号的幂和一元运算符序列中,运算符是从右到左计算的(这不限制操作数的计算顺序):-1**2
结果为 -1
。
幂运算符与内置 pow() 函数具有相同的语义,当使用两个参数调用时:它产生其左参数的右参数的幂。 数字参数首先转换为通用类型。 结果类型是强制转换后的参数类型。
对于混合操作数类型,二元算术运算符的强制规则适用。 对于 int 和 long int 操作数,除非第二个参数为负,否则结果与操作数具有相同的类型(强制转换后); 在这种情况下,所有参数都转换为浮点数,并提供浮点数结果。 例如,10**2
返回 100
,而 10**-2
返回 0.01
。 (这最后一个功能是在 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
*
(乘法)运算符产生其参数的乘积。 参数必须要么都是数字,要么一个参数必须是整数(普通或长)而另一个必须是序列。 在前一种情况下,数字被转换为普通类型,然后相乘。 在后一种情况下,执行序列重复; 负重复因子产生一个空序列。
/
(除法)和 //
(底除法)运算符产生它们参数的商。 数字参数首先转换为通用类型。 普通或长整数除法产生相同类型的整数; 结果是将“floor”函数应用于结果的数学除法。 除以零会引发 ZeroDivisionError
异常。
%
(模)运算符产生第一个参数除以第二个参数的余数。 数字参数首先转换为通用类型。 零右参数引发 ZeroDivisionError
异常。 参数可以是浮点数,例如,3.14%0.7
等于 0.34
(因为 3.14
等于 4*0.7 + 0.34
。)模运算符总是产生相同的结果符号作为其第二个操作数(或零); 结果的绝对值严格小于第二个操作数2的绝对值。
整数除法和模运算符通过以下标识连接:x == (x/y)*y + (x%y)
。 整数除法和取模也与内置函数divmod():divmod(x, y) == (x/y, x%y)
相关联。 这些身份不适用于浮点数; x/y
替换为 floor(x/y)
或 floor(x/y) - 1
3 的情况大致相同。
除了对数字执行模运算之外,%
运算符还被字符串和 unicode 对象重载以执行字符串格式化(也称为插值)。 字符串格式化的语法在 Python 库参考的 字符串格式化操作 部分中进行了描述。
+
(加法)运算符产生其参数的总和。 参数必须要么都是数字,要么都是相同类型的序列。 在前一种情况下,数字被转换为通用类型,然后相加。 在后一种情况下,序列被连接。
-
(减法)运算符产生其参数的差异。 数字参数首先转换为通用类型。
5.7. 移位操作
移位运算的优先级低于算术运算:
shift_expr ::= a_expr | shift_expr ( "<<" | ">>" ) a_expr
这些运算符接受普通或长整数作为参数。 参数被转换为通用类型。 他们将第一个参数向左或向右移动第二个参数给出的位数。
右移 n 位定义为除以 pow(2, n)
。 左移 n 位定义为与 pow(2, n)
的乘法。 负移位计数会引发 ValueError
异常。
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
&
运算符产生其参数的按位与,它必须是普通或长整数。 参数被转换为通用类型。
^
运算符产生其参数的按位异或(异或),它必须是普通或长整数。 参数被转换为通用类型。
|
运算符产生其参数的按位(包括)OR,它必须是普通整数或长整数。 参数被转换为通用类型。
5.9. 比较
与 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
是完全合法的(尽管也许不漂亮)。
形式 <>
和 !=
是等价的; 为与C一致,首选!=
; 其中 !=
在下面提到 <>
也被接受。 <>
拼写被认为是过时的。
5.9.1. 价值比较
运算符 <
、>
、==
、>=
、<=
和 !=
比较两个对象的值. 对象不需要具有相同的类型。
第 章对象、值和类型 说明对象具有值(除了类型和标识之外)。 对象的值在 Python 中是一个相当抽象的概念:例如,对象的值没有规范的访问方法。 此外,不要求对象的值应该以特定方式构造,例如 由其所有数据属性组成。 比较运算符实现了对象值是什么的特定概念。 人们可以将它们视为通过比较实现间接定义对象的值。
类型可以通过实现 __cmp__()
方法或 丰富的比较方法 来自定义它们的比较行为,如 __lt__()
,在 基本自定义 中进行了描述。
相等比较的默认行为(==
和 !=
)基于对象的身份。 因此,相同身份的实例的相等比较导致相等,不同身份的实例的相等比较导致不平等。 这种默认行为的动机是希望所有对象都应该是自反的(即 x is y
意味着 x == y
)。
默认顺序比较(<
、>
、<=
和 >=
)给出一致但任意的顺序。
(这个不寻常的比较定义用于简化诸如排序和 in 和 not in 运算符等操作的定义。 未来,不同类型对象的比较规则可能会发生变化。)
默认相等比较的行为,即具有不同身份的实例总是不相等的,可能与需要具有对象值和基于值的相等的合理定义的类型形成对比。 这些类型需要自定义它们的比较行为,事实上,许多内置类型已经这样做了。
下面的列表描述了最重要的内置类型的比较行为。
内置数字类型(数字类型 — int、float、long、complex)和标准库类型的数量 fractions.Fraction 和 decimal.Decimal可以在它们的类型内部和之间进行比较,但限制是复数不支持顺序比较。 在所涉及类型的限制内,它们在数学上(算法上)进行比较而不会损失精度。
字符串(str 或 unicode 的实例)使用其字符的数字等价物(内置函数 ord() 的结果)按字典顺序进行比较。 4 当比较 8 位字符串和 Unicode 字符串时,将 8 位字符串转换为 Unicode。 如果转换失败,则认为字符串不相等。
tuple 或
list
的实例只能在它们的每个类型内进行比较。 这些类型之间的相等比较会导致不相等,而这些类型之间的排序比较会给出任意顺序。这些序列使用对应元素的比较按字典顺序进行比较,从而强制执行元素的自反性。
在强制元素的自反性时,集合的比较假设对于集合元素
x
,x == x
始终为真。 基于该假设,首先比较元素标识,并且仅对不同的元素执行元素比较。 如果比较的元素是自反的,这种方法会产生与严格元素比较相同的结果。 对于非自反元素,结果与严格元素比较不同。内置集合之间的字典比较工作如下:
两个集合要比较相等,它们必须是相同的类型,具有相同的长度,并且每对对应的元素必须比较相等(例如,
[1,2] == (1,2)
为假,因为类型不相同)。集合的排序与其第一个不相等的元素相同(例如,
cmp([1,2,x], [1,2,y])
返回与cmp(x,y)
相同)。 如果对应的元素不存在,则先对较短的集合进行排序(例如,[1,2] < [1,2,3]
为真)。
映射(dict 的实例)比较相等当且仅当它们具有相等的 (key, value) 对。 键和值的相等比较强制了自反性。
平等以外的结果得到一致解决,但没有另外定义。 5
大多数其他内置类型的对象比较不相等,除非它们是同一个对象; 一个对象被认为比另一个对象小还是大的选择是在程序的一次执行中任意但一致地做出的。
如果可能,自定义其比较行为的用户定义类应遵循一些一致性规则:
平等比较应该是自反的。 换句话说,相同的对象应该比较相等:
x is y
表示x == y
比较应该是对称的。 换句话说,以下表达式应该具有相同的结果:
x == y
和y == x
x != y
和y != x
x < y
和y > x
x <= y
和y >= x
比较应该是可传递的。 以下(非详尽)示例说明:
x > y and y > z
表示x > z
x < y and y <= z
表示x < z
反向比较应该导致布尔否定。 换句话说,以下表达式应该具有相同的结果:
x == y
和not x != y
x < y
和not x >= y
(用于总订购)x > y
和not x <= y
(用于总订购)最后两个表达式适用于完全有序的集合(例如 序列,但不是集合或映射)。 另请参阅 total_ordering() 装饰器。
hash() 结果应该与相等一致。 相等的对象应该具有相同的散列值,或者被标记为不可散列。
Python 不强制执行这些一致性规则。
5.9.2. 会员测试操作
运算符 in 和 not in 测试成员资格。 如果 x 是 s 的成员,则 x in s
计算为 True
,否则计算为 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__()
方法的用户定义类,如果 y.__contains__(x)
返回真值,x in y
返回 True
,否则返回 False
。
对于未定义 __contains__()
但定义 __iter__()
的用户定义类,如果某个值 z
与 x == z
在迭代 y
时产生。 如果在迭代期间引发异常,就好像 in 引发了该异常。
最后,尝试旧式迭代协议:如果一个类定义了 __getitem__()
,则 x in y
是 True
当且仅当存在非负整数索引 i 使得 x == y[i]
和所有较低的整数索引不会引发 IndexError
异常。 (如果引发任何其他异常,就好像 in 引发了该异常)。
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:False
、None
、所有类型的数字零和空字符串和容器(包括字符串、元组、列表、字典、集合和冻结集合)。 所有其他值都被解释为真。 (请参阅 __nonzero__() 特殊方法以了解改变这一点的方法。)
如果参数为假,运算符 not 产生 True
,否则产生 False
。
表达式 x and y
首先计算 x; 如果 x 为假,则返回其值; 否则,计算 y 并返回结果值。
表达式 x or y
首先计算 x; 如果 x 为真,则返回其值; 否则,计算 y 并返回结果值。
(请注意, 和 和 或 都不会限制它们返回的值和类型为 False
和 True
,而是返回最后计算的参数。 这有时很有用,例如,如果 s
是一个字符串,如果它是空的,则应该用默认值替换,表达式 s or 'foo'
会产生所需的值。 因为 not 无论如何都必须创造一个值,所以它不会费心返回与其参数相同类型的值,例如,not 'foo'
产生 False
,而不是 [ X163X]。)
5.11. 条件表达式
2.5 版中的新功能。
conditional_expression ::= or_test ["if" or_test "else" expression] expression ::= conditional_expression | lambda_expr
条件表达式(有时称为“三元运算符”)在所有 Python 操作中的优先级最低。
表达式 x if C else y
首先评估条件,C (not x); 如果 C 为真,则计算 x 并返回其值; 否则,计算 y 并返回其值。
有关条件表达式的更多详细信息,请参阅 PEP 308。
5.12. 拉姆达
lambda_expr ::= "lambda" [parameter_list]: expression old_lambda_expr ::= "lambda" [parameter_list]: old_expression
Lambda 表达式(有时称为 lambda 形式)与表达式具有相同的句法位置。 它们是创建匿名函数的简写; 表达式 lambda parameters: expression
产生一个函数对象。 未命名对象的行为类似于定义为的函数对象
def <lambda>(parameters):
return expression
有关参数列表的语法,请参见 函数定义 部分。 请注意,使用 lambda 表达式创建的函数不能包含语句。
5.13. 表达式列表
expression_list ::= expression ( "," expression )* [","]
包含至少一个逗号的表达式列表产生一个元组。 元组的长度是列表中表达式的数量。 表达式从左到右计算。
尾随逗号仅用于创建单个元组(又名 单例 ); 在所有其他情况下它是可选的。 没有尾随逗号的单个表达式不会创建元组,而是生成该表达式的值。 (要创建一个空元组,请使用一对空括号:()
。)
5.14. 评估顺序
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
5.15. 运算符优先级
下表总结了 Python 中的运算符优先级,从最低优先级(最少绑定)到最高优先级(最绑定)。 同一框中的运算符具有相同的优先级。 除非明确给出语法,否则运算符是二进制的。 同一个盒子中的运算符从左到右分组(除了比较,包括测试,它们都具有相同的优先级和从左到右的链——参见 Comparisons 部分——以及从右到左分组的求幂) .
操作员 | 描述 |
---|---|
lambda
|
Lambda 表达式 |
if – else | 条件表达式 |
or
|
布尔或 |
and
|
布尔与 |
not x
|
布尔非 |
在,不在,是,不是,< ,<= ,> 、>= 、<> 、!= 、==
|
比较,包括成员资格测试和身份测试 |
按位或 | |
^
|
按位异或 |
&
|
按位与 |
<< 、>>
|
转移 |
+ 、-
|
加减 |
* 、/ 、// 、%
|
乘除法余数7 |
+x 、-x 、~x
|
正、负、按位非 |
**
|
求幂 8 |
x[index] 、x[index:index] 、x(arguments...) 、x.attribute
|
订阅、切片、调用、属性引用 |
(expressions...) 、[expressions...] 、{key: value...} 、`expressions...`
|
绑定或元组显示、列表显示、字典显示、字符串转换 |
脚注
- 1
在 Python 2.3 及更高版本中,列表推导式将其包含的每个
for
的控制变量“泄漏”到包含范围中。 但是,此行为已被弃用,依赖它在 Python 3 中不起作用。- 2
虽然
abs(x%y) < abs(y)
在数学上是正确的,但对于浮点数,由于四舍五入,它在数值上可能不正确。 例如,假设 Python 浮点数是 IEEE 754 双精度数的平台,为了-1e-100 % 1e100
与1e100
具有相同的符号,计算结果为 [ X185X],在数值上完全等于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 中的大多数抽象字符仅使用一个代码点表示,但还有许多抽象字符可以另外使用多个代码点的序列表示。 例如,抽象字符“LATIN CAPITAL LETTER C WITH CEDILLA”可以表示为代码位置 U+00C7 处的单个 预组合字符 ,或表示为 基本字符 处的序列代码位置 U+0043(拉丁文大写字母 C),后跟代码位置 U+0327(组合 CEDILLA)处的 组合字符 。
unicode 字符串上的比较运算符在 Unicode 代码点级别进行比较。 这可能与人类的直觉相反。 例如,
u"\u00C7" == u"\u0043\u0327"
是False
,即使两个字符串都表示相同的抽象字符“拉丁大写字母 C WITH CEDILLA”。要在抽象字符级别(即以对人类直观的方式)比较字符串,请使用 unicodedata.normalize()。
- 5
早期版本的 Python 使用排序(键、值)列表的字典比较,但对于比较相等的常见情况,这非常昂贵。 更早的 Python 版本仅通过身份比较字典,但这引起了意外,因为人们希望能够通过将字典与
{}
进行比较来测试字典是否为空。- 6
由于自动垃圾收集、空闲列表和描述符的动态特性,您可能会注意到在 is 运算符的某些使用中看似异常的行为,例如涉及实例方法或常量之间的比较的行为。 查看他们的文档以获取更多信息。
- 7
%
运算符也用于字符串格式化; 相同的优先级适用。- 8
幂运算符
**
的绑定不如其右侧的算术或按位一元运算符紧密,即2**-1
是0.5
。