is是比较“身份”(内存地址)是否相同,==是比较“值”是否相等。
is本质上是通过id()函数比较内存地址,==本质上是调用对象中__eq__()方法比较数据是否相等。
我们来看一个例子:
a = [1, 2]b = [1, 2]print(a is b)print(a == b)
第一个print打印的是False,因为列表a和b实际上是两个不同的列表,id(a)和id(b)是不同的,也就意味着是不同的内存地址;第二个print打印的是True,因为两个列表内容是相同的。
内容相同,is判断的结果就一定是True吗?我们再来看一个例子:
a = 100b = 100print(a is b)c = 258d = 258print(c is d)
这段代码在单独的Python文件中执行打印的结果都是True,但在交互式环境(REPL)中,第二个print打印的结果是False。这是因为在一个Python文件中,Python编译代码时会将相同的常量值存储一份,但在交互式环境中,每一行代码都单独编译,所以c和d是两个不同的对象。
那为什么在交互式环境中第一个print打印是True呢?因为Python解释器在启动的时候会创建一个小整数缓存,范围为-5~256,在这个范围内指的都是同一个对象,超出这个范围创建的都是新的对象。
还有一个字符串的例子,表现也类似:
s1 = 'hello's2 = 'hello'print(s1 is s2)s3 = 'hello,python's4 = 'hello,python'print(s3 is s4)
这段代码在Python文件中两个print打印的都是True,但在交互式环境中第一个print打印的是True,第二个print打印的是False。这涉及到一个概念叫字符串驻留,这是Python中的一种内存优化机制,它把符合标识符规则的字符串只保留一份。
什么叫符合标识符规则?即字符串只包含字母、数字、下划线且不以数字开头。hello这个字符串是符合的,但hello,python是不符合的,因为它包含逗号、感叹号这些特殊字符。
那么可以将复杂的字符串驻留吗?当然也可以,这就需要使用sys.intern():
import syss3 = sys.intern('hello,python')s4 = sys.intern('hello,python')print(s3 is s4)
那么在做对象比较的时候,什么时候用is,什么时候用==呢?
推荐用is的场景:判断是否为None、判断两个变量是否指向同一个对象、判断True/False、与枚举比较。
# 判断是否为Noneif x is None:# 严格判断是否为Trueif flag is True:# 注意区别if flag: # 判断"真值性"(truthy),1、"hello"、[1] 都算 Trueif flag == True: # 1 == True 也是 True(因为 bool 是 int 的子类)if flag is True: # 只有真正的 True 对象才通过# 判断是否指向同一个对象a = [1, 2]b = aif a is b:# 与枚举比较from enum import Enumclass Color(Enum): RED = 1 GREEN = 2c = Color.REDif c is Color.RED:
推荐用==的场景:比较数值、字符串、列表、字典、集合等内容;自定义对象的“逻辑相等”。
# 比较数值if x == 30:# 比较字符串内容if name == "Zhangsan":# 比较列表、字典、集合等if list_a == list_b:if dict_a == dict_b:# 自定义对象逻辑相等class Point: def __init__(self, x, y): self.x = x self.y = y def __eq__(self, other): return self.x == other.x and self.y == other.yp1 = Point(1, 2)p2 = Point(1, 2)print(p1 == p2) # True(值相等)print(p1 is p2) # False(不是同一个对象)
简单来说,判断是否为同一个内存对象用is,判断值是否一样用==。