下面有段代码(以下简称测试例子),你能在不看答案情况下知道运行结果吗?
a = 1
b = 1
print(a is b) # True
print(a == b) # True
a = 10000
b = 10000
print(a is b) # 有时True 有时False
print(a == b) # True
a = -6
b = -6
print(a is b) # 有时True 有时False
print(a == b) # True
a = [1, 5]
b = [1, 5]
c = a
print(a is b) # False
print(a == b) # True
print(c is a) # True
print(c == a) # True
print(c is b) # False
print(c == b) # True
概念
在 Python 中,is
和 ==
都用于比较,是分别根据对象的内存地址和对象的值来判断,所以用途上也不一样。
用图书馆的书籍可以打个值和内存地址形象的比喻:
- 对象的值,就像书籍内容:
- 每本书都有它的内容,例如文字、图片等,这代表了书的“值”。
- 不同的书可能有相同的内容(例如两本相同的书),但它们仍然是不同的实体。
- 对象的内存地址,书籍在图书馆中的位置:
- 每本书在图书馆中都有一个特定的位置,比如在某个书架的某一层某一排。
- 即使两本书的内容完全相同,如果它们在不同的位置上,它们就是两个不同的对象。
is 身份运算符
is 用于比较两个对象的身份,即比较两个对象在内存中的地址(内存地址可以用Python内建函数id()
查看,它会返回一个整数)是否相同。换句话说,它检查的是两个对象是否是同一个对象。
回到开头测试例子里:
# 注意:以下id函数计算出的内存地址整数不一定是代码的那些整数
a = 1
b = 1
print(id(a), id(b)) # 输出:2398302306544 2398302306544
print(a is b) # True,因为a和b的id内存地址相同,所以a和b是同一个对象
a = [1, 5]
b = [1, 5]
print(id(a), id(b)) # 输出:2398357892288 2398357883648
print(a is b) # 输出: False,因为a和b的id内存地址不同,所以a和b是不同的对象,即使它们的值相同
c = a
print(id(a), id(c)) # 输出:2398357892288 2398357892288
print(a is c) # 输出: True,因为c和a的id内存地址相同,所以c和a是同一个对象
== 比较运算符
==
用于比较两个对象的值是否相等。它检查的是对象的值
是否相同,而不是对象本身是否相同(虽然拗口,但事实如此)。 这意味着即使是两个不同的对象,只要它们的值相同,== 比较的结果就是 True。
a = 1
b = 1
print(a == b) # 输出: True,因为 a 和 b 的值相同
a = [1, 5]
b = [1, 5]
c = a
print(a == b) # 输出: True,因为 a 和 b 的值相同
print(c == b) # 输出: True,因为 c 和 b 的值相同
通常情况下,==
可以通过重载 __eq__
方法来定义值比较行为, 再举回书籍的例子,我们用书籍内容content
来表示对象的值,location
不是内存地址
class Book:
def __init__(self, location, content):
self.location = location
self.content = content
def __eq__(self, other):
if isinstance(other, Book):
return self.content == other.content
return False
def __repr__(self):
return f"Book(content='{self.content}')"
# 创建两个 Book 对象
book1 = Book(location="A", content="Hello")
book2 = Book(location="B", content="Hello")
book3 = Book(location="C", content="Hi")
# 比较对象
print(book1 == book2) # 输出: True,因为 content 相同
print(book1 == book3) # 输出: False,因为 content 不同
# 查看对象内存地址,不同的对象
print(id(book1)) # 输出: 1907430631760
print(id(book2)) # 输出: 1907430632240
print(id(book3)) # 输出: 1907430632336
注意
共用缓存对象
你也许发现了is
在某些情况下判断并不稳定,比如测试例子中a
、b
都等于10000
(或-6
)时,a is b
有时候会等于False
,有时候会等于True
。
这主要是因为Python
对于一些小整数(通常在-5
到256
之间,范围并不是固定)和一些字符串进行了缓存,所以这些对象在内存中会被重用。 当比较这些小整数和字符串时,is
比较时可能返回 True
,即使你认为它们是不同的对象。
所以涉及到这些对象进行比较时,一般不用is
而是==
。
单例模式
在单例模式下,所有构造出来的对象实际上都是同一个实例,因此使用 is
和 ==
进行比较时,结果都应该是 True
,但一般都是用is
来判断。
这是因为使用 is
来判断更加直观和高效, is
比较的是对象的身份(即内存地址),而 ==
比较的是对象的值,要先找到地址才能找到值,而且某些值比较时需要进行额外计算消耗资源(参考找书籍的例子)。
在python中有些比较特殊的对象如None
、True
、False
等,它们都是个单例对象,所以通常判断一个变量是否为这些对象时应该使用 is
而不是 ==
。
应用场景
-
is
主要用于判定是不是同一个对象,在与单例对象比较时,一般都是有is
来判断 -
==
通常是用于两个对象确切的值的比较,比如字符串比较,数值比较。
总结
-
==
:比较两个对象的值是否相等。 -
is
:比较两个对象的身份(内存地址)是否相同。