Blog

Python 中 is 和 == 的区别

Python 中 is 和 == 的区别:一个问两个名字是否指向同一个对象,另一个问值是否相等。另外讲讲为什么小整数看起来不守这条规则。

== 问的是两个对象的值是否相同。is 问的是它们是不是内存里的同一个对象。

两者结果一致的情况很多,用错了通常也能跑,正因如此,出问题时才是很难查的 bug。

区别

内容相同的两个列表相等,但不是同一个对象。

# == compares values, is compares identity
a = [1, 2, 3]
b = [1, 2, 3]
print(a == b, a is b)

c = a
print(a == c, a is c)

输出:

True False
True True

把一个赋值给另一个,两个判断就都为真,因为只有一个列表。

判断 None 用 is

None 是单例,全局只有一个,所以该问的是标识。

# for None, always use is
value = None
print(value is None, value == None)

输出:

True True

== None 通常能用,但仍然是错的,原因见下一节。

相等可以被重新定义

类可以让 == 表示任何意思。is 无法重写。

# == can be redefined, is cannot
class Always:
    def __eq__(self, other):
        return True

x = Always()
print(x == 'anything', x == 42)
print(x is Always())

输出:

True True
False

如果拿 == None 来比较这个对象,判断会通过,你处理 None 的代码就会在一个真实对象上运行。

为什么小整数看起来是同一个对象

CPython 会缓存小整数,同一个代码对象里的常量也是共享的。于是 is 会在你以为是 False 的时候返回 True。

# small integers are cached by CPython
m, n = 256, 256
print('256:', m is n)
p = 1000
q = 1000
print('1000 literals:', p is q)          # same code object, so the constant is shared
r = int('1000')
print('1000 at runtime:', p is r, p == r)

输出:

256: True
1000 literals: True
1000 at runtime: False True

最后一行才说了实话。以同样方式写出的两个 1000 在这里共享一个对象,运行时构造出来的 1000 却不共享。这些都不是语言保证的行为,所以千万别写依赖它的比较。

字符串也一样

字符串字面量会被驻留(intern),运行时构造的字符串不会。

# the same happens with short strings
s1 = 'hello'
s2 = 'hello'
print('literal:', s1 is s2)
s3 = ''.join(['hel', 'lo'])
print('built at runtime:', s1 is s3, s1 == s3)

输出:

literal: True
built at runtime: False True

所以 if name is "admin" 在测试时能通过,到了生产环境就失败,因为那时名字是从请求里传进来的。

标识能告诉你对象有没有被修改

这是 is 有用的一面。它能看出一个操作是创建了新对象,还是改动了原来那个。

# identity is what actually changed
first = [1, 2]
second = first
first = first + [3]         # rebinds, new object
print(first, second, first is second)

third = [1, 2]
fourth = third
third += [3]                # mutates in place
print(third, fourth, third is fourth)

输出:

[1, 2, 3] [1, 2] False
[1, 2, 3] [1, 2, 3] True

+ 构建了新列表,并把名字重新绑定过去,所以第二个名字看到的还是旧值。+= 原地修改了列表,所以两个名字都能看到变化。屏幕上的结果一样,但对其他持有引用的代码来说,行为完全不同。

要点

  • == 比较值,is 比较标识。
  • is 只用于 NoneTrueFalse 和哨兵对象。
  • 小整数和字面量字符串的缓存是 CPython 的实现细节,别在上面构建逻辑。
  • 想检查对象是被复制了还是被修改了,就用 is

正因如此,代码检查工具会标记拿 is 和字面量比较的写法。如果你用的工具没有这项检查,就把它打开。

这篇文章对你有帮助吗?

点一颗爱心来评分!

平均评分 0 / 5. 投票总数: 0

还没有人投票。来做第一个评分的人吧。