“==”和“is”有区别吗?

Is there a difference between "==" and "is"?

提问人:Bernard 提问时间:9/25/2008 最后编辑:martineauBernard 更新时间:2/4/2022 访问量:507798

问:

这个问题的答案是社区的努力。编辑现有答案以改进此帖子。它目前不接受新的答案或交互。

我的 Google-fu 让我失望了。

在 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

Python 引用 相等 语义

评论


答:

48赞 stephenbayer 9/25/2008 #1

==确定值是否相等,同时确定它们是否为完全相同的对象。is

12赞 mmaibaum 9/25/2008 #2

https://docs.python.org/library/stdtypes.html#comparisons

is身份测试 平等测试==

每个(小)整数值都映射到一个值,因此每 3 个值都是相同且相等的。这是一个实现细节,但不是语言规范的一部分

1146赞 Torsten Marek 9/25/2008 #3

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

请同时查看此问题

评论

2赞 ahuigo 7/23/2018
我发现: 输出: .echo 'import sys;tt=sys.argv[1];print(tt is "foo", tt == "foo", id(tt)==id("foo"))'| python3 - fooFalse True False
0赞 Gabriel Staples 1/23/2019
你让我失去了切片运算符列表复制部分,所以我编辑了你的答案,在那里发表评论。看起来我刚刚达到了在申请之前不必审查我的编辑的门槛,所以希望这对你来说很酷。无论如何,这里有一个有用的参考资料,说明如何复制我遇到的列表,并且必须参考才能弄清楚你在做什么:stackoverflow.com/a/2612815/4561887b = a[:]
14赞 Ahmed Fasih 9/8/2019
1000 is 10**3在 Python 3.7 中计算为 True,因为 10**3 是类型。但计算结果为 False,因为 1e3 是类型。int1000 is 1e3float
3赞 chepner 5/27/2020
@AhmedFasih 是否为真取决于实现,并取决于编译器对表达式的预求值。 计算结果为 。1000 is 10**310**3x=10; 1000 is x**3False
4赞 Santosh Kumar 1/11/2022
只是一个旁注。Python 3.8 及更高版本在比较文字(如 1000 是 10**3)时返回 SyntaxWarning:SyntaxWarning: "is" with a literal. Did you mean "=="?
7赞 David Webb 9/25/2008 #4

你的答案是正确的。运算符比较两个对象的标识。运算符比较两个对象的值。is==

对象的标识一旦创建,就永远不会改变;您可以将其视为对象在内存中的地址。

您可以通过定义方法或丰富的比较方法(如 )来控制对象值的比较行为。__cmp____eq__

21赞 Dan Lenski 9/26/2008 #5

它们完全不同。 检查对象标识,同时检查是否相等(一个依赖于两个操作数类型的概念)。is==

幸运的是,“”似乎可以正确地处理小整数(例如 5 == 4+1)。这是因为 CPython 通过使整数成为单例来优化整数(-5 到 256)范围内的存储。这种行为完全依赖于实现,不能保证在各种小的转换操作下都能得到保留。is

例如,Python 3.5 还将短字符串设置为单例,但对它们进行切片会破坏此行为:

>>> "foo" + "bar" == "foobar"
True
>>> "foo" + "bar" is "foobar"
True
>>> "foo"[:] + "bar" == "foobar"
True
>>> "foo"[:] + "bar" is "foobar"
False
5赞 cobbal 7/6/2009 #6

看看 Stack Overflow 问题 Python 的“is”运算符在整数中表现得出乎意料

它主要归结为“”检查它们是否是同一个对象,而不仅仅是彼此相等(低于 256 的数字是特例)。is

401赞 John Feminella 7/6/2009 #7

有一个简单的经验法则可以告诉您何时使用或 。==is

  • ==是为了价值平等。当您想知道两个对象是否具有相同的值时,请使用它。
  • is参考平等。当您想知道两个引用是否引用同一对象时,请使用它。

通常,当您将某些东西与简单类型进行比较时,通常会检查值是否相等,因此您应该使用 .例如,您的示例的目的可能是检查 x 的值是否等于 2 (),而不是从字面上引用是否与 2 相同的对象。====x


还有一点需要注意:由于 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

评论

2赞 AsheKetchum 1/31/2019
关于 和 的第一个例子,只是想指出,如果将 和设置为 [-5, 256] 之间的中间符,实际上返回 。更多信息请见:stackoverflow.com/q/306313/7571052a=500b=500aba is bTrue
1赞 John Feminella 2/1/2019
@AsheKetchum,是的,请注意,我写了“事实证明,出于性能原因,Python 的参考实现将 -5..256 范围内的整数对象缓存为单例实例。
0赞 10/30/2020
就像在这里添加我的两点一样。如果你想检查它是否是同一个对象,请使用(假设你有一个城市和路线对象的列表;你可以比较位置,或者只是检查它是否是同一个城市 - 所以这里的比较更强)。否则,如果您只关心基元,通常就足够了。这更像是一条经验法则,当事情变得艰难时,就会被违反isis==
3赞 John Machin 7/6/2009 #8

正如 John Feminella 所说,大多数时候你会使用 == 和 !=,因为你的目标是比较值。我只想对你剩下的时间会做什么进行分类:

NoneType 有一个且只有一个实例,即 None 是单例。因此和意思相同。但是,测试速度更快,Pythonic 约定是使用 .foo == Nonefoo is Noneisfoo is None

如果你正在做一些反省或垃圾回收,或者检查你的定制字符串实习小工具是否正常工作或类似,那么你可能有一个用例。foobar

True 和 False 也是(现在)单例,但没有用例,也没有用例。foo == Truefoo is True

评论

1赞 Ted Brownlow 7/10/2022
对于 和 是不同的。foo=1foo==Truefoo is True
61赞 Russia Must Remove Putin 1/6/2018 #9

和 在 Python 中有什么区别吗?==is

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

==:检查相等性 - 语义是等效对象(不一定是同一对象)将测试是否相等。正如文档所说

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

IS:检查身份 - 语义是对象(保存在内存中)对象。同样,文档说

运算符和对象标识测试:为 true 当且仅当 和 是同一个对象。对象标识是 使用函数确定。 产生相反的结果 真值。isis notx is yxyid()x is not y

因此,标识检查与对象 ID 是否相等相同。那是

a is b

等同于:

id(a) == id(b)

其中 是返回一个整数的内置函数,该整数“保证在同时存在的对象中是唯一的”(请参阅),并且 where 和 是任意对象。idhelp(id)ab

其他使用说明

您应该使用这些比较来了解它们的语义。用于检查身份和检查平等性。is==

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

用例包括:is

  • None
  • 枚举值(使用枚举模块中的枚举时)
  • 通常为模块
  • 通常是由类定义生成的类对象
  • 通常是函数定义产生的函数对象
  • 内存中只应存在一次的任何其他内容(通常都是单例)
  • 按标识所需的特定对象

通常的用例包括:==

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

一般用例,同样,对于 ,是您想要的对象可能不是同一个对象,而可能是等效的对象==

PEP 8 个方向

PEP 8 是标准库的官方 Python 风格指南,也提到了两个用例

与单例的比较应该始终使用 或 ,而不是相等运算符。Noneisis not

另外,当你真正想写的时候要小心—— 例如,在测试默认为 的变量或参数是否设置为其他值时。另一个值可能具有类型(例如 作为容器),这在布尔上下文中可能是假的!if xif x is not NoneNone

从身份推断平等

如果为真,通常可以推断出相等性——从逻辑上讲,如果一个对象是它自己,那么它应该测试为等价于它自己。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

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

哇,这可能会导致一些阴险的错误。我有一些代码可以检查 a 是否是 b,它按我想要的方式工作,因为 a 和 b 通常是小数字。这个错误在生产六个月后才在今天发生,因为 a 和 b 终于大到无法缓存。– GWG公司

它在开发中起作用。它可能已经通过了一些单元测试。

它在生产中起作用 - 直到代码检查大于 256 的整数,此时它在生产中失败。

这是一个生产失败,可能在代码审查中被发现,或者可能在样式检查器中被捕获。

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

评论

0赞 Jean-François Fabre 7/24/2019
“根本不使用”也是一个好规则。惯用语是个例外,但话虽如此也有效......is None== None
0赞 Arthur 2/11/2020
@Jean-FrançoisFabre 另一个例外:官方文档似乎建议用于比较 s。isEnum
0赞 plasma 8/6/2020
这是否意味着两个自定义类进行比较,例如 a = Car(“new”),B=Car(“new”),如果我们使用 a==b,这等于 a 是 b,我说得对吗?
0赞 Russia Must Remove Putin 8/6/2020
@UniSize 我不认为你是对的。问题:1.你说a和B,但你说a==b(小写),Python区分大小写。2. 你没有说 A 和 B 是否应该是等价的,但两个具有相同参数的实例意味着它们是等价的,但不是相同的对象,因此你的解释是错误的。请在这里重新阅读我的回答,让我知道缺少什么,并在您的回答中小心和明确。
0赞 plasma 8/7/2020
@AaronHall 我的抱歉,首先,这是一个错别字。我修改了它,a = Car(“new”),b=Car(“new”),如果我们使用 a==b,这是否等于 a is b?我知道“is”是检查两个对象是否在同一个内存位置,a==b 是两个对象之间的比较。根据测试,a==b 返回 false,a is b 也返回 false。在相同的初始化下,为什么 a==b 会返回 false?
26赞 MSeifert 1/20/2018 #10

和 和有什么不一样?is==

==和不同的比较!正如其他人已经说过的:is

  • ==比较对象的值。
  • is比较对象的引用。

在Python中,名称引用对象,例如在本例中,并引用存储值的实例:value1value2int1000

value1 = 1000
value2 = value1

enter image description here

因为指的是同一个对象,并且会给出:value2is==True

>>> value1 == value2
True
>>> value1 is value2
True

在以下示例中,名称 和 引用不同的实例,即使两者都存储相同的整数:value1value2int

>>> value1 = 1000
>>> value2 = 1000

enter image description here

因为存储的相同值(整数)将是 ,这就是为什么它通常被称为“值比较”。但是会返回,因为这些是不同的对象:==TrueisFalse

>>> value1 == value2
True
>>> value1 is value2
False

何时使用哪个?

一般来说,这是一个更快的比较。这就是为什么 CPython 缓存(或者重用会是更好的术语)某些对象(如小整数、某些字符串等)的原因。但这应该被视为实现细节,可以(即使不太可能)在没有警告的情况下随时更改。is

只有在以下情况下,才应使用 is

  • 想要检查两个对象是否真的是同一个对象(而不仅仅是相同的“值”)。例如,如果使用单例对象作为常量。

  • 想要将值与 Python 常量进行比较。Python 中的常量是:

    • None
    • True1
    • False1
    • NotImplemented
    • Ellipsis
    • __debug__
    • 类(例如或int is intint is float)
    • 内置模块或第三方模块中可能有其他常量。例如,来自 NumPy 模块的 np.马.masked

在所有其他情况下,您应该使用 == 来检查是否相等。

我可以自定义行为吗?

在其他答案中没有提到某些方面:它是 Python“数据模型”的一部分。这意味着可以使用 __eq__ 方法自定义其行为。例如:==

class MyClass(object):
    def __init__(self, val):
        self._value = val

    def __eq__(self, other):
        print('__eq__ method called')
        try:
            return self._value == other._value
        except AttributeError:
            raise TypeError('Cannot compare {0} to objects of type {1}'
                            .format(type(self), type(other)))

这只是一个人为的例子,以说明该方法实际上是调用的:

>>> MyClass(10) == MyClass(10)
__eq__ method called
True

请注意,默认情况下(如果在类或超类中找不到其他实现)使用:__eq____eq__is

class AClass(object):
    def __init__(self, value):
        self._value = value

>>> a = AClass(10)
>>> b = AClass(10)
>>> a == b
False
>>> a == a

因此,如果您想要“更多”而不仅仅是自定义类的引用比较,那么实现实际上很重要!__eq__

另一方面,您无法自定义检查。只有当您具有相同的参考时,它才会进行比较。is

这些比较是否总是返回布尔值?

因为可以重新实现或覆盖,所以不限于 return 或 .它可以返回任何内容(但在大多数情况下,它应该返回一个布尔值!__eq__TrueFalse

例如,对于 NumPy 数组,将返回一个数组:==

>>> import numpy as np
>>> np.arange(10) == 2
array([False, False,  True, False, False, False, False, False, False, False], dtype=bool)

但是支票总是会返回或!isTrueFalse


1 正如亚伦·霍尔(Aaron Hall)在评论中提到的:

通常,您不应该执行任何 OR 检查,因为通常在将条件隐式转换为布尔值的上下文中使用这些“检查”(例如在语句中)。因此,进行比较隐式布尔强制转换比只执行布尔强制转换要做更多的工作 - 并且您将自己限制为布尔值(这不被认为是 pythonic)。is Trueis Falseifis True

就像 PEP8 提到的那样:

不要将布尔值与 进行比较或使用 。TrueFalse==

Yes:   if greeting:
No:    if greeting == True:
Worse: if greeting is True:

评论

2赞 Russia Must Remove Putin 1/20/2018
我将不得不不同意您将“常量”与 - 指向布尔值的名称应使用布尔上下文进行检查 - 例如或 .你永远不应该做 or - 此外,常量只是一个常量语义值,而不是单例,因此在这种情况下检查 with 在语义上是不正确的。我挑战你找到一个来源来支持你的断言——我认为你不会找到一个。isif __debug__:if not __debug__:if __debug__ is True:if __debug__ == True:is
0赞 MSeifert 1/20/2018
@AaronHall 是什么让你认为常量不是单例?请注意,只有 、 和 是你所说的“常量语义值”,因为它们不能被重新赋值。但他们都是单身人士。NoneTrueFalse__debug__
0赞 Russia Must Remove Putin 1/20/2018
阅读 PEP 8 - Ctrl-F 并查找单词“更糟”。- 如果你是单元测试,你会使用 self.assertTrue
1赞 MSeifert 1/20/2018
@AaronHall 在某些情况下,您确实需要 or 检查(但是是的,这些非常罕见 - 但如果您这样做,您可以使用 )。这就是为什么 CPython 有时也会使用它们(例如这里或这里is Trueif Falseis)
1赞 OverLordGoldDragon 6/16/2020
为什么比 ?会失败吗?如果有什么事情更有可能失败,可以被覆盖为废话,但不是.is True== TrueTrue is True== True__eq__is
5赞 imanzabet 2/1/2018 #11

由于这篇文章中的其他人详细回答了比较对象或变量之间的差异的问题,我主要强调字符串之间的比较,可以给出不同的结果,我会敦促程序员谨慎使用它们。==isis==

对于字符串比较,请确保使用代替:==is

str = 'hello'
if (str is 'hello'):
    print ('str is hello')
if (str == 'hello'):
    print ('str == hello')

外:

str is hello
str == hello

但在下面的示例中,会得到不同的结果:==is

str2 = 'hello sam'
    if (str2 is 'hello sam'):
        print ('str2 is hello sam')
    if (str2 == 'hello sam'):
        print ('str2 == hello sam')

外:

str2 == hello sam

结论与分析:

请小心比较字符串。 由于用于比较对象,并且在 Python 3+ 中每个变量(例如字符串)都解释为对象,因此让我们看看上面段落中发生了什么。isis

在 python 中,有一个 id 函数,它显示对象在其生命周期内的唯一常量。此 id 在 Python 解释器的后端使用关键字比较两个对象。is

str = 'hello'
id('hello')
> 140039832615152
id(str)
> 140039832615152

str2 = 'hello sam'
id('hello sam')
> 140039832615536
id(str2)
> 140039832615792

评论

0赞 akash 6/6/2018
为什么“is”“对带空格的字符串是这样工作的?
0赞 Rida Shamasneh 2/20/2019
根据前面的答案:似乎 python 对小整数和字符串执行缓存,这意味着它在此代码快照中对“hello”字符串使用相同的对象引用,而它没有对“hello sam”进行缓存,因为它比“hello”大(即它管理“hello sam”字符串的不同引用, 这就是为什么“is”运算符在后面的示例中返回 false 的原因)如果我错了,请纠正我
0赞 imanzabet 6/21/2020
@AkashGupta很抱歉延迟回复。我只是添加了一些解释,为什么这件事发生在 Python 解释器中。我希望它会有所帮助。
2赞 user2761895 2/1/2022
我正在使用 Python 3.9 并抛出 这告诉我们我们需要用于字符串比较,这很方便。str is 'hello'SyntaxWarning: "is" with a literal. Did you mean "=="?==
3赞 Sandeep 3/7/2018 #12

他们中的大多数人已经回答了这一点。作为附加说明(基于我的理解和实验,但不是来自记录在案的来源),声明

== 如果变量引用的对象相等

从上面的答案应该读作

== 如果变量引用的对象相等且对象属于同一类型/类

.我根据以下测试得出了这个结论:

list1 = [1,2,3,4]
tuple1 = (1,2,3,4)

print(list1)
print(tuple1)
print(id(list1))
print(id(tuple1))

print(list1 == tuple1)
print(list1 is tuple1)

在这里,列表和元组的内容相同,但类型/类不同。

5赞 suvojit_007 7/30/2018 #13

简而言之,检查两个引用是否指向同一对象。 检查两个对象是否具有相同的值。is==

a=[1,2,3]
b=a        #a and b point to the same object
c=list(a)  #c points to different object 

if a==b:
    print('#')   #output:#
if a is b:
    print('##')  #output:## 
if a==c:
    print('###') #output:## 
if a is c:
    print('####') #no output as c and a point to different object