=="和"is"有什么区别?

628 投票
13 回答
525060 浏览
提问于 2025-04-11 09:20

我的谷歌搜索能力失败了。

在Python中,下面这两个判断是否相等的方式是一样的吗?

n = 5
# Test one.
if n == 5:
    print 'Yay!'

# Test two.
if n is 5:
    print 'Yay!'

对于一些对象,比如说一个list,这种判断是否成立呢?

好的,这个有点回答到我的问题了:

L = []
L.append(1)
if L == [1]:
    print 'Yay!'
# Holds true, but...

if L is [1]:
    print 'Yay!'
# Doesn't.

所以==是用来比较值的,而is是用来检查它们是否是同一个对象,对吗?

13 个回答

61

Python中的==is有什么区别吗?

有的,它们之间有一个非常重要的区别。

==:用于检查相等性 - 这意味着两个对象的值相同,即使它们不是同一个对象,也会被认为是相等的。正如文档中所说

运算符<、>、==、>=、<=和!=比较两个对象的值。

is:用于检查身份 - 这意味着在内存中,某个对象就是那个对象。再次引用文档

运算符isis not用于测试对象的身份:x is y只有在xy是同一个对象时才为真。对象的身份是通过id()函数来确定的。x is not y则返回相反的真值。

因此,检查身份实际上就是检查对象的ID是否相同。也就是说,

a is b

等同于:

id(a) == id(b)

其中id是一个内置函数,它返回一个整数,这个整数在同时存在的对象中是“保证唯一”的(可以查看help(id)),而ab是任意对象。

其他使用方向

你应该根据它们的语义来使用这些比较。使用is来检查身份,使用==来检查相等性。

所以一般来说,我们用is来检查身份。这通常在我们检查某个对象在内存中应该只存在一次时很有用,这种对象在文档中被称为“单例”。

使用is的情况包括:

  • None
  • 枚举值(使用枚举模块中的Enums时)
  • 通常的模块
  • 通常的类对象(来自类定义)
  • 通常的函数对象(来自函数定义)
  • 任何其他在内存中应该只存在一次的对象(一般都是单例)
  • 你想要的特定对象

使用==的常见情况包括:

  • 数字,包括整数
  • 字符串
  • 列表
  • 集合
  • 字典
  • 自定义可变对象
  • 大多数情况下的其他内置不可变对象

再次强调,==的常见用例是你想要的对象可能不是同一个对象,而是一个等效的对象。

PEP 8 指导

PEP 8,Python标准库的官方风格指南,也提到两个is的使用场景

与单例(如None)的比较应该始终使用isis not,而不是相等运算符。

此外,当你真正想表达if x is not None时,注意不要写成if x -- 例如,当测试一个默认值为None的变量或参数是否被设置为其他值时。其他值可能有一个类型(比如容器),在布尔上下文中可能会被视为假!

从身份推断相等性

如果is为真,通常可以推断出相等性 - 从逻辑上讲,如果一个对象就是它自己,那么它应该被认为与自己相等。

在大多数情况下,这个逻辑是成立的,但它依赖于__eq__特殊方法的实现。正如文档中所说

相等性比较(==!=)的默认行为是基于对象的身份。因此,具有相同身份的实例的相等性比较结果为相等,而具有不同身份的实例的相等性比较结果为不相等。这个默认行为的动机是希望所有对象都应该是自反的(即x是y意味着x==y)。

为了保持一致性,文档建议:

相等性比较应该是自反的。换句话说,相同的对象应该比较为相等:

x is y意味着x == y

我们可以看到,这也是自定义对象的默认行为:

>>> class Object(object): pass
>>> obj = Object()
>>> obj2 = Object()
>>> obj == obj, obj is obj
(True, True)
>>> obj == obj2, obj is obj2
(False, False)

反之亦然,通常也是成立的 - 如果两个对象测试为不相等,通常可以推断它们不是同一个对象。

由于相等性测试可以被自定义,这个推断并不总是适用于所有类型。

一个例外

一个显著的例外是nan - 它总是被测试为不等于它自己:

>>> nan = float('nan')
>>> nan
nan
>>> nan is nan
True
>>> nan == nan           # !!!!!
False

检查身份通常比检查相等性要快得多(后者可能需要递归检查成员)。

但在你可能发现多个对象是等效的情况下,身份检查不能替代相等性检查。

注意,比较列表和元组的相等性时,会假设对象的身份是相等的(因为这是一个快速检查)。如果逻辑不一致,这可能会导致矛盾 - 就像nan的情况:

>>> [nan] == [nan]
True
>>> (nan,) == (nan,)
True

一个警示故事:

这个问题试图用is来比较整数。你不应该假设一个整数的实例与通过另一个引用获得的实例是同一个。这段故事解释了原因。

有个评论者的代码依赖于Python中小整数(-5到256,包括在内)是单例的这一事实,而不是检查相等性。

哇,这可能导致一些隐蔽的错误。我有段代码检查a是否等于b,这样工作得很好,因为a和b通常是小数字。这个错误直到今天才发生,经过六个月的生产使用,因为a和b终于大到不再被缓存。 – gwg

这在开发中是有效的。可能通过了一些单元测试。

而且在生产中也有效 - 直到代码检查一个大于256的整数,这时它在生产中失败了。

这是一个在代码审查或可能的风格检查中可以发现的生产失败。

让我强调一下:不要用is来比较整数。

403

这里有一个简单的规则,可以帮助你判断什么时候使用 ==is

  • == 是用来判断 值是否相等。当你想知道两个对象的值是否相同时,就用它。
  • is 是用来判断 引用是否相等。当你想知道两个引用是否指向同一个对象时,就用它。

一般来说,当你在比较简单类型的东西时,通常是在检查 值是否相等,所以应该使用 ==。比如,你的例子可能是想检查 x 的值是否等于 2(用 ==),而不是检查 x 是否真的指向与 2 相同的对象。


还有一点需要注意:由于 CPython 的实现方式,如果你错误地使用 is 来比较整数的引用相等性,你可能会得到意想不到和不一致的结果:

>>> a = 500
>>> b = 500
>>> a == b
True
>>> a is b
False

这基本上是我们预期的结果:ab 的值相同,但它们是不同的实体。那么这个呢?

>>> c = 200
>>> d = 200
>>> c == d
True
>>> c is d
True

这个结果和之前的不一致。到底发生了什么?原来,Python 的引用实现会缓存 -5 到 256 之间的整数对象,作为单例实例,以提高性能。下面是一个示例来说明这一点:

>>> for i in range(250, 260): a = i; print "%i: %s" % (i, a is int(str(i)));
... 
250: True
251: True
252: True
253: True
254: True
255: True
256: True
257: False
258: False
259: False

这又是一个明显的理由,不要使用 is:当你错误地用它来判断值是否相等时,行为会依赖于具体的实现。

1155

is 会返回 True,如果两个变量指向的是同一个对象(在内存中),而 == 则是用来判断这两个变量所指向的对象是否相等。

>>> a = [1, 2, 3]
>>> b = a
>>> b is a 
True
>>> b == a
True

# Make a new copy of list `a` via the slice operator, 
# and assign it to variable `b`
>>> b = a[:] 
>>> b is a
False
>>> b == a
True

在你的例子中,第二个测试之所以有效,是因为 Python 会缓存小整数对象,这属于实现细节。对于较大的整数,这种方法就不适用了:

>>> 1000 is 10**3
False
>>> 1000 == 10**3
True

字符串字面量也是一样的道理:

>>> "a" is "a"
True
>>> "aa" is "a" * 2
True
>>> x = "a"
>>> "aa" is x * 2
False
>>> "aa" is intern(x*2)
True

请查看 这个问题,了解更多信息。

撰写回答