“ ==”和“是”之间有区别吗?


630

我的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进行测试以查看它们是否是同一对象?

Answers:


927

isTrue如果两个变量指向同一个对象(==如果变量引用的对象相等),则将返回。

>>> 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
我发现:echo 'import sys;tt=sys.argv[1];print(tt is "foo", tt == "foo", id(tt)==id("foo"))'| python3 - foo输出:False True False
ahuigo '18

您因b = a[:]切片运算符列表副本部分而迷失了我,所以我已经编辑了您的答案以在其中添加评论。看起来我已经达到了必须在编辑之前不进行审查的门槛,所以希望对您来说很酷。无论如何,这是关于如何复制我遇到的列表并必须参考以找出您在做什么的有用参考:stackoverflow.com/a/2612815/4561887
Gabriel Staples,

证明差异的另一种方法是比较不同类型的对象,当然,它们可能永远不是同一对象,但使用时仍然比较相等==。因此5.0,例如,是浮点值,5而是整数。但是5.0 == 5仍然会返回,True因为它们表示相同的值。就性能和鸭子类型而言,is始终由解释器通过比较操作数的内存地址来进行测试,而==由对象决定它是否将自己定义为与其他对象相等。
Bachsau

3
1000 is 10**3由于10 ** 3是type,因此在Python 3.7中计算结果为True int。但是1000 is 1e3由于1e3是type,因此求值为False float
艾哈迈德·法西

@AhmedFasih是否1000 is 10**3为true与实现有关,取决于编译器对表达式的预评估10**3x=10; 1000 is x**3评估为False
chepner

311

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

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

通常,在将某事物与简单类型进行比较时,通常会检查值是否相等,因此应使用==。例如,您的示例的目的可能是检查x是否具有等于2(==)的值,而不是检查x字面上是否指向与2相同的对象。


其他注意事项:由于CPython参考实现的工作方式,如果错误地用于is比较整数的参考相等性,则会得到意外且不一致的结果:

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

这几乎是我们所期望的:a并且b具有相同的值,但是是不同的实体。但是呢?

>>> 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:当您错误地将其用于值相等时,该行为应由实现决定。


至于第一个例子a=500b=500,只是想指出的是,如果你设置ab为[-5,256]之间的整数,a is b实际的回报True。此处的更多信息:stackoverflow.com/q/306313/7571052
AsheKetchum

1
@AsheKetchum,是的,请注意,我写了“出于性能原因,Python的参考实现将-5..256范围内的整数对象作为单例实例进行了缓存。”
约翰·费米内拉19'Jan


32

==isPython 之间有区别吗?

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

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

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

is:检查身份-语义是对象(保存在内存中)对象。再次,文档说

运算符isis not对象身份测试:x is y当且仅当xy是相同对象时,才为true 。使用该id()功能确定对象身份。x is not y产生反真值。

因此,对身份的检查与对对象ID的相等性检查相同。那是,

a is b

是相同的:

id(a) == id(b)

where id是返回整数的内建函数,该整数“保证同时存在的对象之间是唯一的”(请参阅​​参考资料help(id)),而where ab则是任意对象。

其他使用说明

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

因此,通常,我们使用is来检查身份。当我们检查一个仅在内存中存在一次的对象(在文档中称为“单个”)时,这通常很有用。

用例is包括:

  • None
  • 枚举值(当使用枚举模块中的枚举时)
  • 通常是模块
  • 通常是由类定义产生的类对象
  • 通常由函数定义产生的函数对象
  • 在内存中应该只存在一次的所有其他内容(通常是所有单例)
  • 您希望通过身份获得的特定对象

通常的用例==包括:

  • 数字,包括整数
  • 清单
  • 词典
  • 自定义可变对象
  • 在大多数情况下,其他内置的不可变对象

一般使用情况下,再次对==,就是你想可能不是对象相同的对象,相反,它可能是一个相当于一个

PEP 8方向

PEP 8,标准库的官方Python样式指南还提到了以下两个用例is

与单例之类的比较None应始终使用isis not,而不应使用相等运算符。

另外,当心if x您的意思if x is not None,例如当测试是否将默认None 设置为的变量或参数设置为其他值时,请当心编写。另一个值可能具有在布尔上下文中可能为false的类型(例如容器)!

从身份推断平等

如果is为true,通常可以推断出相等性-从逻辑上讲,如果对象是自身,则它应该测试为等同于自身。

在大多数情况下,此逻辑是正确的,但它依赖于__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)在Python中是单例,而不是检查是否相等。

哇,这可能会导致一些隐患。我有一些检查a是否为b的代码,它可以按我的意愿工作,因为a和b通常很小。该错误仅在生产六个月后才出现在今天,因为a和b最终足够大而无法缓存。– gwg

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

它可以在生产中使用-直到代码检查出大于256的整数为止,此时它在生产中失败了。

这是生产失败,可能已在代码审查中或可能通过样式检查器捕获。

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


“完全不使用”也是一个好规则。该地道is None是个例外,但该== None作品太...
让·弗朗索瓦·法布尔

@Jean-FrançoisFabre另一个例外:官方文档似乎建议使用s is进行比较Enum
亚瑟

@Arthur我已经添加了用例列表...
Aaron Hall

19

is和之间有什么区别==

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

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

在Python中,例如,在这种情况下value1,名称指的是对象,并value2指代int存储值的实例1000

value1 = 1000
value2 = value1

在此处输入图片说明

因为value2引用相同的对象is==将给出True

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

在以下示例中,名称value1value2引用不同的int实例,即使它们都存储相同的整数:

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

在此处输入图片说明

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

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

什么时候使用?

通常,is比较起来要快得多。这就是为什么CPython缓存(或者最好是重用)某些对象,例如小整数,某些字符串等。但是,这应该被视为实现细节,即使在没有警告的情况下也可以随时更改(即使可能性很小)。

is应在以下情况下使用

  • 想要检查两个对象是否真的是同一对象(不仅仅是相同的“值”)。一个示例可以是如果使用单例对象作为常量。
  • 想比较一个值和一个Python 常量。Python中的常量为:

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

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

我可以自定义行为吗?

==在其他答案中还没有提到某些方面:它是Python“ Data model”的一部分。这意味着可以使用该__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检查。它总是会比较公正,如果你有相同的参考。

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

由于__eq__可以重新实现或覆盖,因此不限于return TrueFalse。它可以返回任何内容(但是在大多数情况下,它应该返回一个布尔值!)。

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

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

但是is支票总是会返回TrueFalse


1正如亚伦·霍尔在评论中提到的那样:

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

就像PEP8提到的那样:

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

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

2
我将不得不不同意您的断言,以将“常量”与is-指向布尔值的名称进行比较,应使用布尔上下文(如if __debug__:或)进行检查if not __debug__:。您永远都不应这样做,if __debug__ is True:或者if __debug__ == True:-进一步来说,常量只是常量语义值,而不是单例,因此is在这种情况下进行核对在语义上是不正确的。我向您提出挑战,要求您找到支持您主张的资料来源-我认为您不会找到任何依据。
亚伦·霍尔

@AaronHall是什么让您认为常量不是单例呢?请注意,只有NoneTrueFalse而且__debug__是你所说的“常语义值”,因为它们不能被重新分配。但是它们都是单例。
MSeifert

阅读PEP 8 -Ctrl-F并查找单词“ worse”。-如果您要进行单元测试,则可以使用self.assertTrue
亚伦·霍尔

@AaronHall在某些情况下,您确实需要is Trueor进行if False检查(但是,是的,这很少见-但是如果您执行了操作,则可以使用来进行操作is)。这就是为什么即使CPython有时也会使用它们的原因(例如,在这里在这里
MSeifert

19

他们是完全不同的is检查对象身份,同时==检查是否相等(一个概念取决于两个操作数的类型)。

幸运的巧合是“ is”似乎可以正确地使用小整数(例如5 == 4 + 1)。那是因为CPython通过使整数成为单例来优化整数存储范围(-5到256)。此行为完全取决于实现,并且不能保证在所有较小的转换操作方式下都可以保留该行为。

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

>>> "foo" + "bar" == "foobar"
True
>>> "foo" + "bar" is "foobar"
True
>>> "foo"[:] + "bar" == "foobar"
True
>>> "foo"[:] + "bar" is "foobar"
False


6

您的回答是正确的。该is运算符比较两个对象的身份。该==操作比较两个对象的值。

一旦创建了对象,其身份就不会改变。您可能会认为它是对象在内存中的地址。

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



3

简而言之,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 

2

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

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

如果您要对垃圾收集进行自省或处理,或者检查自定义构建的字符串实习小工具是否正常工作,则可能有一个用例foo是is bar

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


2

他们中的大多数人已经回答了这一点。正如补充说明(基于我的理解和实验,但不是来自书面记录)的声明

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

从上面的答案应该理解为

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

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

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)

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


2

is和equals(==)之间的Python区别

is运算符可能看起来与相等运算符相同,但它们并不相同。

is检查两个变量是否指向同一对象,而==符号检查两个变量的值是否相同。

因此,如果is运算符返回True,则相等性肯定为True,但相反的情况可能为True,也可能不是True。

这是一个演示相似性和差异性的示例。

>>> a = b = [1,2,3]
>>> c = [1,2,3]
>>> a == b
True
>>> a == c
True
>>> a is b
True
>>> a is c
False
>>> a = [1,2,3]
>>> b = [1,2]
>>> a == b
False
>>> a is b
False
>>> del a[2]
>>> a == b
True
>>> a is b
False
Tip: Avoid using is operator for immutable types such as strings and numbers, the result is unpredictable.

1
对于从其他来源引用的文本,请仅使用块引号,此时您必须包括署名(请参阅stackoverflow.com/help/referencing)。如果这是您自己的文字,请删除块引号。
马丁·彼得斯

1

当这篇文章中的其他人详细回答了这个问题时,我将主要强调字符串之间的比较is以及可以给出不同结果的== 字符串,我敦促程序员谨慎使用它们。

为了进行字符串比较,请确保使用==代替is

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

出:

str is hello
str == hello

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

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

出:

str == hello sam

结论:

is谨慎使用以比较字符串


为什么“ is”对于带空格的字符串会像这样工作?
Akash Gupta

根据先前的答案:似乎python在小整数和字符串上执行缓存,这意味着它在此代码快照中针对“ hello”字符串出现使用了相同的对象引用,而实际上并未对“ hello sam”进行缓存相对大于'hello'(即,它管理'hello sam'字符串的不同引用,这就是为什么'is'运算符在后面的示例中返回false的原因)如果我错了,请更正我
Rida Shamasneh
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.