Python为什么是“is None”判断而不是“== None”?

回答标题问题前,先看下面这段代码(以下简称测试例子),你能在不看答案情况下知道运行结果吗?

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()查看,它会返回一个整数)是否相同。

换句话说,is检查的是两个对象是否是同一个对象。回到开头测试例子里:

# 注意:以下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在某些情况下判断并不稳定,比如测试例子中ab都等于10000(或-6)时,a is b有时候会等于False,有时候会等于True

这是因为Python对于一些小整数(通常在-5256之间,范围并不是固定)和一些字符串进行了缓存,所以这些对象在内存中会被重用

这会导致当比较这些小整数和字符串时,is 比较时可能返回 True。因此涉及到这些对象进行比较时,一般不用is,而是==

单例模式

在单例模式下,所有构造出来的对象实际上都是同一个实例,因此使用 is== 进行比较时,结果都会是 True

class SingletonMeta(type):
    _instances = {}

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super().__call__(*args, **kwargs)
        return cls._instances[cls]


class MyClass(metaclass=SingletonMeta):
    pass


a = MyClass()
b = MyClass()
print(a is b)  # 输出:True
print(a == b)  # 输出:True

单例对象一般都是用is来判断, 这是因为使用 is 来判断更加直观和高效, is比较本质是两个内存地址的整数值比较,而 == 在某些值比较时需要进行额外计算消耗资源,比如字符串/可变对象值比较都比整数值比较更耗性能。

这也是为什么在与NoneTrueFalse等特殊的单例对象比较时,都建议使用 is,而不是 ==

应用场景

  • is主要用于判定是不是同一个对象,在与单例对象比较时,一般都是有is来判断。
  • ==通常是用于两个对象确切的值的比较,比如字符串比较,数值比较、数组比较等。

总结

  • ==:比较两个对象的是否相等。
  • is:比较两个对象的内存地址是否相同。

如果你觉得本文有帮助到你,非常期待得到你的支持和鼓励;如果有其他问题或补充,欢迎评论区留言交流。

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值