5. 表达式

优质
小牛编辑
136浏览
2023-12-01

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将被转换成_Hamspam。这种转换与标识符使用的语法上下文无关。如果转换后的名称过长(超过255个字符),将可能发生与具体实现有关的截断。如果类的名称只由下划线组成,则不会转换。

5.2.2. 字面值

Python支持字符串字面值和各种数值字面值:

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

在浮点数和虚数(复数)情况下,可能只是近似值。详细信息参见字面值一节。

所有的字面值都是不可变数据类型,因此对象的ID不如它的值重要。多次计算具有相同值的字面值(无论是程序文本中相同的出现还是不同的出现)得到的既可能是同一个对象也可能是具有相同值的不同对象。

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*yforxinrange(10)foryinbar(x))。

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

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”子句。当推导执行时,产生的键和值以它们生成的顺序插入到新的字典中。

键的类型的限制在前面的标准类型的层次一节中有列出。(简要地讲,键的类型应该是可哈希的,即排除所有可变的对象。)重复的键之间的冲突不会被检测到;一个给定的键的最后的值(表示式中最右边的值)将获胜。

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 表达式

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

2.5 版中新增。

yield表达式只用于定义生成器函数,且只能用于函数的定义体中。在函数定义中使用yield表达式就可以充分使得该定义创建一个生成器函数而不是普通的函数。

当调用生成器函数时,它返回一个称为生成器的迭代器。然后该生成器控制生成器函数的执行。当调用生成器的其中一个方法时,执行开始。此时,执行会行进到第一个yield表达式,在那里执行被挂起并返回expression_list的值给生成器的调用者。挂起的意思是保存所有的局部状态,包括当前局部变量的绑定、指令的指针和内部的计算栈。当通过调用生成器的一个方法来恢复执行时,函数可以准确地继续执行就好像yield表达式只是一个外部的调用。恢复执行后yield表达式的值取决于恢复执行的方法。

所有这些使得生成器函数与协程非常类似;它们可以yield多次,它们有多个入口点且它们的执行可以挂起。唯一的区别是生成器函数不可以控制yield之后执行应该从何处继续;控制始终被转让给生成器的调用者。

5.2.10.1. 生成器迭代器的方法

该小节讲述生成器迭代器的方法。它们可用于控制生成器函数的执行。

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

classgeneratorgenerator.next() 开始生成器函数的执行或者在最后一次执行的yield表达式处恢复执行。当生成器函数使用next()方法恢复执行时,当前的yield表达式始终None。然后执行继续行进到下一个yield表达式,在那里生成器被再次挂起并返回expression_list的值给next()的调用者。如果生成器退出时没有yield另外一个值,则引发一个StopIteration异常。

.generator.send(value) 恢复执行并“发送”一个值到生成器中。该value参数成为当前yield表达式的结果。send()方法返回生成器yield的下一个值,如果生成器退出时没有yield另外一个值则引发StopIteration。当调用send()用于开始生成器的执行时,它必须以None作为参数进行调用,因为没有接受该值的yield表达式。

generator.throw(type[, value[, traceback]]) 在生成器暂停的地方引发一个type类型的异常,并返回生成器函数yield的下一个值。如果生成器在退出时没有yield一个值,则引发StopIteration异常。如果生成器函数没有捕获传递进来的异常或者引发一个不同的异常,那么该异常将传播到调用者。

generator.close() 在生成器函数暂停的地方引发一个GeneratorExit。如果生成器函数此后引发StopIteration(正常退出或者由于已经正在关闭)或者GeneratorExit(没有捕获该异常),close会返回到调用者。如果生成器yield一个值,则引发一个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.

另请参阅

PEP 0342 - 通过增强的生成器实现协程增强生成器API和语法的提议,使得它们可以作为简单的协程使用。

5.3. 初级操作

初级操作表示语言中绑定性最高的操作。它们的语法是:

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

5.3.1. 属性引用

属性引用是一个初级操作,后面跟随一个句号和一个名称:

attributeref ::=  primary "." identifier

primary必须是一个支持属性引用类型的对象,例如模块、列表和实例。接着该对象被要求生成名称为identifier的属性。如果该属性不可访问,则抛出AttributeError异常。否则,生成的对象的类型和值取决于该对象。对相同属性的多次求值可能产生不同的对象。

5.3.2. 下标

下标选择序列(字符串、元组或列表)或者映射(字典)对象的一个元素:

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

primary必须是一个序列或者映射类型的对象。

如果primary是一个映射,那么expression_list必须是一个对象,其值为映射的一个键,该下标选择映射中对应于该键的值。(expression_list是一个元组除非它只有一个元素。)

如果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         ::=  "..."

这里的形式语法有歧义:expession_list看上去也像slice_list,所以任何下标也可以解释为切片。为了不引入更复杂的语法,通过定义在这种情况下解释为下标优先于解释为切片来消除歧义(如果slice_list不包含proper_slice和ellipse也属于这种情况)。类似地,当slice_list只有一个short_slice且没有末尾的逗号时,解释为简单切片要优先于解释为扩展切片。

简单切片的语义如下。primary必须是一个序列对象。下界和上界表达式,如果存在,必须是普通的整数;默认分别是零和sys.maxint。如果有一个是负数,则将它加上序列的长度。切片选择所有索引为k的元素,其中i<=k<j且ij是指定的下界和上界。它可能是一个空的序列。如果i或者j位于合法的索引范围之外不会出错(这些元素不存在所以它们不会被选择)。

扩展切片的语法如下。primary必须是一个映射对象,它以从slice_list构造的键做索引,如下所示。如果slice_list包含至少一个逗号,则键是一个包含slice_item转换的元组;否则,long_slice作为键。slice_item是一个表达式时,转换就是那个表达式。slice_item是ellipsis时,转换为内建的Ellipsis对象。proper_slice的转换是一个切片对象(参阅标准类型的层次),它的start、stop和step属性分别是表达式lower_bound、upper_bound和stride的值,没有的表达式用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 ["," "*" expression] ["," "**" expression]
                          | "**" expression
positional_arguments ::=  expression ("," expression)*
keyword_arguments    ::=  keyword_item ("," keyword_item)*
keyword_item         ::=  identifier "=" expression

在位置参数和关键字参数之后可以存在一个末尾的逗号而不影响语义。

primary必须是一个可调用的对象(用户定义的函数、内建的函数、内建对象的方法、类对象、类实例的方法以及某些类实例自己也是可调用的;Python的扩展可以定义额外的可调用对象类型)。所有的参数表达式都将在调用发生之前求值。关于形式参数列表的语法请参考函数的定义一节。

如果存在关键字参数,它们首先被转换为位置参数,如下所述。首先,创建一个没有填充的空位序列用于形参。如果有N个位置参数,则它们被放置在前N个空位中。下一步,对于每个关键字参数,用标识符决定对应的位置(如果标识符与第一个形参的名称相同,则使用第一个位置,以此类推)。如果该位置已经被填充,则引发一个TypeError异常。否则,将该参数的值放入该位置并填充它(即使该表达式是None,它也会填充该位置)。当处理完所有的参数时,仍然没有填充的位置将用来自函数定义的对应默认值填充。(默认值只在函数定义时计算一次;因此,用于默认值的可变对象例如列表或字典将被所有没有指定对应位置参数值的调用共享;通常应该避免这点。)如果有没有填充的空位且没有指定默认值,则引发一个TypeError异常。否则,使用这些填满的位置作为调用的参数序列。

CPython实现细节:一种实现可以提供这样的内建函数,它的位置参数没有名称,因此不可以通过关键字提供,即使它们由于文档的需要而被“命名”。在CPython中,那些用C实现并使用PyArg_ParseTuple()解析参数的函数就是这种情况。

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

关键字参数和*expression语法都在同一个调用中使用很不常见,所以实践中这种困惑不会出现。

如果**expression语法出现在函数调用中,那么expression必须是一个映射,它的内容将被当作额外的关键字参数。某个关键字在expression和显式的关键字参数中都出现的情况下,将引发TypeError异常。

使用identifier或者*identifier语法的形参不可以作为位置参数的槽位或者关键字参数的名称使用。使用(sublist)语法的形参不可以作为关键字参数的名称使用;最外层的sublist对应于单一的一个未命令参数槽位,在所有其它参数的处理完成之后参数的值使用普通的元组赋值规则赋值给sublist。

一个调用永远返回某个值,即使可能为None,除非它引发一个异常。该值如何计算取决于可调用对象的类型。

如果它是—

一个用户定义的函数: 该函数的代码块将被执行,并将参数列表传递给它。代码块所做的第一件事是将绑实参与参数定;这在函数的定义一节有讲述。当代码块执行一条return时,它表示该函数调用的返回值。

一个内建的函数或方法: 结果取决于解释器;内建函数和方法的描述请参见内建的函数

一个类对象: 返回该类的一个新的实例。

一个类实例的方法: 调用对应的用户自定义的函数,参数列表比调用的参数列表多一个元素:实例成为第一个参数。

一个类实例: 该类必须定义一个call()方法;效果和调用该方法一样。

5.4. 乘方操作符

乘方操作符的绑定性比它左侧的一元操作符高;比它右侧的一元操作符绑定性低。其语法是:

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

因此,在一个没有括号的乘方和一元操作符序列中,操作符从右向左计算(这不会约束操作数的计算顺序):-1**2的结果是-1。

乘方操作符的语义与用两个参数调用内建的pow()函数相同:它产生左侧参数的右侧参数次幂。其数值参数首先被转换成相同的类型。结果的类型是强制转换后的参数类型。

操作符的类型不一样时,运用二元算术操作符的强制转换规则。对于整数和长整数,结果的类型与(强制转换后的)操作数类型相同,除非第二个参数为负数;在这种情况下,所有的参数被转换成浮点数并返回浮点数结果。例如,102返回100,但是10-2返回0.01。(最后的这个特性在Python2.2中添加。在Python2.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

*(乘法)操作符产生它参数的乘积。其参数必须都是数值,或者一个是整数(普通整数或长整数)另外一个是序列。在前一种情况下,数值会被转换成一个相同的类型然后一起相乘。在后一种情况下,将进行序列的重复操作;负的重复值将产生一个空的序列。

/(除法)和//(整除)操作符产生它们参数的商。其数值参数首先被转换成相同的类型。普通整数或者长整数除法产生一个相同类型的整数;其结果是在算术除法的结果上调用“取整”函数。除以零将引发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库参考的字符串格式化一节讲述。

2.3版后废弃的内容:整除操作符、取模操作符和divmod()函数不再为复数定义。作为替代,如果合适,使用将abs()函数将其转换为浮点数。

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

-(减法)操作符产生其参数差。其参数首先被转换成相同的类型。

5.7. 移位操作

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

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

这些操作符接收普通整数或者长整数作为参数。参数会被转换成一种共同的类型。它们将第一个参数向左或向右移动第二个参数指出的位数。

右移n位定义为除以pow(2,n)。左移n位定义为乘以pow(2,n)。负的移位数目会引发ValueError异常。

在当前的实现中,要求右操作数至多为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

&操作符产生按位与,它的参数必须是普通整数或长整数。参数会被转换成一种共同的类型。

^操作符产生按位异或,它的参数必须是普通整数或长整数。参数会被转换成一种共同的类型。

|操作符产生按位或,它的参数必须是普通整数或长整数。参数会被转换成一种共同的类型。

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<yandy<=z,但是y只计算一次(在两种情况中当发现x<y为假时都不会再计算z)。

形式上,如果a, b, c, ..., y, z是表达式且op1, op2, ..., opN是操作数,那么aop1bop2c...yopNz等同于aop1bandbop2cand...yopNz,不同点是每个表达式值至多计算一次。

注意aop1bop2c并不意味着ac 之间会有比较,所以xz 是完全合法的(尽管不漂亮)。

<>和!=两种形式时等同的;为了与C保持一致,倾向于使用!=;下面提到!=的地方同样接受<> 。拼写<>被认为是废弃的。

操作符<、>、==、>=、<=和!= 比较两个对象的值。对象不需要具有相同的类型。如果两个都是数字,它们将被转会成一个共同的类型。否则,不同类型的对象将永远是不相等的,虽然顺序是固定的但是是随机的。你可以通过定义cmp方法或者像gt这样更丰富的比较方法来控制非内建类型对象的比较行为,在Special method names一节有详细的描述。

(这种不常见的比较定义用于简化像排序这种操作的定义以及in和notin操作符。未来,不同类型对象的比较规则可能会改变。)

相同类型对象的比较取决于它们的类型:

  • 数字按照算术意义比较。

  • 字符串使用字符对应的数值(内建函数ord()的结果)按字典序比较。 Unicode和八比特字符完全适用这种行为。[4]

  • 元组和列表通过比较对应的项按字典序比较。这意味着若要比较结果相等,每一个元素比较的结果必须相等且两个序列的类型必须相同并且具有相同的长度。

如果不相等,序列按照它们第一个不同的元素排序。例如,cmp([1,2,x],[1,2,y])的返回值与cmp(x,y)相同。如果对应的元素不存在,则长度较短的序列排在第一个(例如[1,2]<[1,2,3])。

  • 映射(字典)当且仅当它们排好序的(键,值)列表相等时才相等。[5]虽然当不相等时的结果是固定的,但是具体是怎样的结果是没有定义的。[6]

  • 其它大部分内建类型的对象是不相等的除非它们是相同的对象;一个对象是小于还是大于另外一个对象的抉择虽然是随机的但是在程序的一次执行中是一致的。

操作符in和notin用于测试成员资格。如果xs 的一个成员那么xins为真,否则为假。xnotins返回xins的否定式。成员资格测试传统用于序列上;如果序列包含一个元素与某对象相等则该对象是这个序列的成员。然而,其它许多类型的对象不用称为序列而支持成员资格测试也是合理的。特别地,字典(针对键)和集合就支持成员关系测试。

对于列表和元组类型,xiny 为真当且仅当存在一个索引i 使得x==y[i]为真。

对于Unicode和字符串类型,xiny为真当且仅当xy 的一个子串。

版本2.3 中的改变:之前,x要求是一个长度为1的字符串。

对于定义了contains()方法的用户自定义类,xiny为真当且仅当y.contains(x)为真。

对于没有定义contains()但定义iter()的用户自定义类,xiny为真如果某个值z在迭代y时满足x==z。如果迭代过程中抛出异常,就好像是in抛出那个异常一样。

最后,尝试旧式的迭代协议:如果一个类定义了getitem(),xiny为真当且仅当有一个非负的整数索引i使得x==y[i], 且更小的索引不会引发IndexError异常。(如果引发了其它异常,则像是in引发了该异常)。

notin操作符定义为取与in相反的真值。

is 和isnot操作测试对象的ID:xisy当且仅当xy是相同的对象时为真。xisnoty产生相反的真值。[7]

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、None、所有类型中的数值零、空的字符串和容器(包括字符串、元组、字典和固定集合)。其它所有的值都解释为真。(请参见nonzero() 特殊方法以获得改变这种行为的方式。)

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

表达式xandy首先计算x;如果x为假,则返回它的值;否则,再计算y 并返回结果的值。

表达式xory首先计算x;如果x为真,则返回它的值;否则,再计算y 并返回结果值。

(注意and和or的返回值都不局限于False和True,而是返回最后计算的参数结果。这在有些时候是有用的,例如,如果s是一个字符串,当它是空的时候应该被一个默认值替换,那么表达式sor'foo'就可以产生想要的值。因为not无论如何必须生成一个值,它不会设法返回和其参数类型相同的值,所以not'foo'生成False, 而非''。)

5.11. 条件表达式

在版本2.5 中新引入。

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

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

表达式xifCelsey首先计算条件C(而非not**x);如果C为真,则计算x 并返回它的值;否则,计算y 并返回它的值。

关于条件表达式的更多细节,请参见PEP 308

5.12. Lambda 表达式

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

Lambda表达式(有时叫做lambda形式)具有和表达式相同的地位。它们是创建匿名函数的一种快捷方式;表达式lambdaarguments:expression生成一个函数对象。此命名对象的行为类似下面定义的函数对象

def name(arguments):
    return expression

关于参数列表的语法,请参见函数定义一节。注意lambda表达式创建的函数不可以包含语句。

5.13. 表达式序列

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

至少包含一个逗号的表达式序列产生一个元组。元组的长度是列表中表达式的个数。表达式按从左到右的顺序计算。

尾部的逗号仅仅在创建单元素元组(又叫独元)时需要;在其它所有情况下,它都是可选的。