前言
Python 中对象的比较有两种方式 == 和 is。两种方式都能判断操作符两侧的变量值是否相等,那么它们的区别是什么呢?通过下面的介绍我们来一探究竟。
比较操作符通常用于条件语句,如下示例:
if a == b: pass
if a is False: pass
== 与 is 的区别
== 操作符比较对象的值是否相等。小明有一块 劳力士 手表,小李也有一块同款 劳力士 手表,这时我们就认为这两块手表相等。
小明的手表 = 劳力士
小李的手表 = 劳力士
小明的手表 == 小李的手表
is 操作符比较对象的身份标识是否相等,即对象在内存中的地址是否相同,如果两个对象的身份标识相等,就说明它们是同一个对象。小明的爸爸称呼小明叫 儿子,小明的女朋友称呼小明叫 老公,但这两个称呼都代表 小明 这个人,即为同一个对象。
爸爸的儿子 = 小明
女朋友的老公 = 小明
爸爸的儿子 is 女朋友的老公
接下来就用代码来展示一下 == 与 is 的区别:
> a = [1, 2, 3] > b = [1, 2, 3] > a == b True > a is b False > id(a) 24603664 > id(b) 24603144
> a = [1, 2, 3] > b = a > a == b True > a is b True > id(a) 24604144 > id(b) 24604144
在 Python 中 id 函数接收一个对象作为参数,并返回该对象在内存中的地址。
由以上代码可以分析出:== 操作符只比较两个对象的值是否相等,但不比较两个对象是否为同一个对象;而 is 操作符并不是比较两个对象的值是否相等,而是会确认两个对象是否为同一个对象,如果为同一个对象,那么它们的值自然相等。
Python 小整数对象池
以上两段代码已经能够体现出 == 与 is 的区别,不过 Python 中也有一些特殊情况,来看下面例子:
> a = 5 > b = 5 > a == b True > a is b True > id(a) 1730274128 > id(b) 1730274128
> a = 257 > b = 257 > a == b True > a is b False > id(a) 48558688 > id(b) 48558720
以上代码看起来就很怪异了,同样的比较操作,只是换了一个数字结果就不同了。
其实出现以上结果的原因在于 Python 自身。Python 出于性能上的考虑,在解释器启动的时候就已经将 -5 到 256 的整数创建到内存中了。而当我们需要创建值在 -5 到 256 的 int 数字的时候,Python 并不会新开辟一块内存去创建数字,而是直接将已存在的对象返回。
但是如果新创建的数字不在这个范围,Python 就会为每个变量单独开辟自己的内存空间。
Python intern 机制
再来看下面关于字符串比较的例子:
> a = 'hello world' > b = 'hello world' > a == b True > a is b False > id(a) 49465408 > id(b) 49465448
> a = 'hello' > b = 'hello' > a == b True > a is b True > id(a) 49429152 > id(b) 49429152
想必根据之前数字比较的例子,你大概也能猜测到以上代码结果不同的原因了。事实上,以上结果同样是 Python 出于对性能的考虑,不过这次 Python 并没有预先将 hello 字符串创建到内存中,而是使用了一种叫 intern 的机制。
关于 intern 机制在这里我们不去深究,以后有机会专门写一篇博客来介绍。总之你需要知道在某些场景下,Python 会对字符串开启 intern 机制来提高性能,从而导致出现上面示例代码的结果。
== 与 is 各自的适用场景
什么时候用 ==、什么时候用 is 呢?
当我们需要比较一个变量与一个 单例 的时候,应该使用 is,其他情况通常使用 ==。
例如拿一个变量去跟 True 或 False 进行比较的时候就应该使用 is,因为用 is 的比较的速度要比用 == 更快。
用 is 比较对象的时候,只需要判断它们是否处于同一块内存地址即可,而用 == 比较更慢的原因在于当用 == 去比较对象的时候会调用对象的 __eq__() 方法,而 __eq__() 方法通常会被重载,执行其内部逻辑往往会多花一些时间。
以下就是一个重载对象 __eq__() 方法的例子:
class MyList(object): def __init__(self, *args): self._list = [*args] def __eq__(self, other): result = False for i in self._list: for j in other._list: if i == j: break else: break else: result = True return result li_1 = MyList(1, 2, 3) li_2 = MyList(1, 2, 3) print(li_1 == li_2) # True
你可以自行尝试修改 __eq__() 方法内部的逻辑来观察其结果。