比较操作符用来判断同类型对象是否相等,所有的内建类型均支持比较运算,比较运算返回布尔值True或False。如果你正在使用的是早于Python2.3的版本,因为这些版本还没有布尔类型,所以会看到比较结果为整型值1 (代表True)或0 (代表False)。
注意,实际进行的比较运算因类型而异。换言之,数字类型根据数值的大小和符号比较,字符串按照字符序列值进行比较,等等。
不同于很多其他语言,多个比较操作可以在同一行上进行,求值顺序为从左到右。
我们会注意到比较操作是针对对象的值进行的,也就是说比较的是对象的数值而不是对象本身。在后面的部分我们会研究对象身份的比较。
未来很有可能不再支持<>操作符,建议你一直使用!=操作符
作为对值比较的补充,Python也支持对象本身的比较。对象可以被赋值到另一个变量(通过引用)。因为每个变量都指向同一个(共享的)数据对象,只要任何一个引用发生改变,该对象的其他引用也会随之改变。
为了方便大家理解,最好先别考虑变量的值,而是将变量名看作对象的一个链接。让我们来看以下三个例子:
例1: foo1和foo2指向相同的对象
foo1 = foo2 = 4.3
当你从值的观点看这条语句时,它表现的只是一个多重赋值,将4.3这个值赋给了 fool和foo2这两个变量。这当然是对的,不过它还有另一层含义。事实是一个值为4.3的数字对象被创建,然后这个对象的引用被赋值给fool和foo2,结果就是fool和foo2指向同一个对象。图4-1演示了一个对象两个引用。
图 4-1 foo1和foo2指向相同的对象
例2: foo1和foo2指向相同的对象
foo1 = 4.3
foo2 = foo1
这个例子非常类似上一个,一个值为4.3的数值对象被创建,然后赋给一个变量,当执行foo2 = foo1时,foo2被指向foo1所指向的同一个对象,这是因为Python通过传递引用来处理对象。foo2就成为原始值4.3的一个新的引用。这样foo1和foo2就都指向了同一个对象。示意图也和图4-1一样。
例3: fool和foo2指向不同的对象
foo1 = 4.3
foo2 = 1.3 + 3.0
这个例子有所不同。首先一个数字对象被创建,然后赋值给foo1。然后第二个数值对象被创建并赋值给foo2。尽管两个对象保存的是同样大小的值,但事实上系统中保存的都是两个独立的对象,其中foo1是第一个对象的引用,foo2则是第二个对象的引用。图4-2演示给我们这里有两个不同的对象,尽管这两个对象有同样大小的数值。我们为什么在示意图中使用盒子?没错,对象就像一个装着内容的盒子。当一个对象被赋值到一个变量,就像在这个盒子上贴了一个标签,表示创建了一个引用。每当这个对象有了一个新的引用,就会在盒子上新贴一张标签。当一个引用被销毁时,这个标签就会被撕掉。当所有的标签都被撕掉时,这个盒子就会被回收。那么,Python是怎么知道这个盒子有多少个标签呢?
图 4-2 foo1和foo2指向不同的对象
每个对象都天生具有一个计数器,记录它自己的引用次数。这个数目表示有多少个变量指向该对象。这也就是我们在3.5.5〜3.5.7小节提到的引用计数。Python提供了 is和is not操作符来测试两个变量是否指向同一个对象。像下面这样执行一个测试。
a is b
这个表达式等价于下面的表达式。
id(a) == id(b)
对象身份比较操作符拥有同样的优先级,表4.2列出了这些操作符。在下面这个例子里,我们创建了一个变量,然后将第二个变量指向同一个对象。
is与 not标识符都是Python关键字。
核心笔记:实践
在上面的例子中,你会注意到我们使用的是浮点型而不是整型。为什么会这样?整型对象和字符串对象是不可变对象,所以Python会很高效地缓存它们。这会造成我们认为Python应该创建新对象时,它却没有创建新对象的假象。请看下面的例子。
在上面的例子中,a和b指向了相同的整型对象,但是c和d并没有指向相同的浮点型对象。如果我们是纯粹主义者,我们会希望a与b能和c与d—样,因为我们本意就是为了创建两个整型对象,而不是像b = a这样的结果。
Python仅缓存简单整型,因为它认为在Python应用程序中这些小整型会经常被用到。当我们在写作本书的时候,Python缓存的整型范围是(-1,100),不过这个范围是会改变的,所以请不要在你的应用程序使用这个特性。
Python2.3中决定,在预定义缓存字符串表之外的字符串,如果不再有任何引用指向它,那这个字符串将不会被缓存。也就是说,被缓存的字符串将不会像以前那样永生不灭,对象回收器一样可以回收不再被使用的字符串。从Python 1.5起提供的用于缓存字符的内建函数intern()也已经不再推荐使用,即将被废弃。
布尔逻辑操作符and、or和not都是Python关键字,这些操作符的优先级按从高到低的顺序列于表4.3。not操作符拥有最高优先级,只比所有比较操作符低一级。and和or操作符则相应地再低一级。
前面我们提到过Python支持一个表达式进行多种比较操作,其实这个表达式本质上是由多个隐式的and连接起来的多个表达式。