12

自从我开始学习 python 已经有几天了,那时我偶然发现了==and is。来自java背景,我假设==通过对象ID和is值进行比较,但是做

 >>> a = (1,2)
 >>> b = (1,2)
 >>> a is b
 False
 >>> a == b
 True

似乎is相当于 java 的==,python==相当于 java 的equals(). 这是思考is和之间区别的正确方法==吗?还是有警告?

4

4 回答 4

22
  • '==' 检查是否相等,
  • 'is' 检查身份

也可以看看

为什么在 Python 中使用 '==' 或 'is' 比较字符串有时会产生不同的结果?

于 2013-01-03T18:15:49.300 回答
16

is检查两个操作数是否是同一个对象。==调用__eq__()左操作数,传递右操作数。通常,此方法实现相等比较,但可以编写一个将其用于其他目的的类(但绝不应该)。

请注意,在某些实现中,对于某些对象(字符串文字、介于 -1is==256 之间的整数)会给出相同的结果,但这并不意味着在这些情况下应该将运算符视为可替换的。

于 2013-01-03T18:20:19.040 回答
2

跟进@CRUSADER 的回答:

==使用eq方法检查对象的相等性。

is检查对象的实际内存位置。如果它们是相同的内存位置,则测试为True

如上所述,前 2**8 个整数存储在内存位置以提高速度,因此要查看发生了什么,请使用 256 以上的其他对象或整数。例如:


In [8]: a = 1001
In [9]: b = a # this sets a pointer to a for the variable b
In [10]: a == b 
Out[10]: True # of course they are equal
In [11]: a is b 
Out[11]: True # and they point to the same memory location
In [12]: id(a)
Out[12]: 14125728
In [13]: id(b)
Out[13]: 14125728

In [14]: b = 1001 #this instantiates a new object in memory In [15]: a == b Out[15]: True In [16]: a is b Out[16]: False #now the memory locations are different In [17]: id(a) Out[17]: 14125728 In [18]: id(b) Out[18]: 14125824

于 2013-01-03T20:31:11.480 回答
1

这是看似同义的概念可能会使新程序员感到困惑的情况之一,例如我第一次写这个答案时。您与基于 Java 的假设很接近,但倒退了。这些运算符之间的区别归结为对象等效与对象身份的问题,但与您假设的相反,==按值is比较并按对象 id 比较。来自 cpython 的内置文档(通过help("is")在我的解释器提示下键入获得,但也可在此处在线获得):

身份比较 =====================

运算符“是”和“不是”测试对象身份:当且仅当xy是同一个对象时,“x 是 y”为真。对象身份是使用“id()”函数确定的。“x is not y”产生逆真值。

为了让经验不足的程序员(或任何需要复习的人)稍微了解一下,每个概念的粗略定义如下:

  • 对象等效性:如果两个引用具有相同的有效值,则它们是等效的。

  • 对象标识:如果两个引用指向同一个确切的对象,例如相同的内存位置,则它们是相同的

对象等价发生在您可能预期的大多数情况下,例如比较2 == 2[0, None, "Hello world!"] == [0, None, "Hello world!"]. 对于内置类型,这通常是根据对象的值来确定的,但是用户定义的类型可以通过定义__eq__方法来定义自己的行为(尽管仍然建议以反映对象的完整值的方式这样做)物体)。对象同一性是可以导致对等的东西,但总的来说,完全是一个单独的问题。对象标识严格取决于 2 个对象(或者更确切地说,2 个引用)是否引用内存中完全相同的对象,由id(). 关于相同引用的一些有用说明:因为它们引用内存中的相同实体,所以它们总是(至少在 cpython 中)具有相同的值,并且除非__eq__被非常规地定义,因此将是等价的。如果您尝试通过就地操作(例如list.append()or )更改其中一个引用,这甚至成立my_object[0]=6,并且应注意测试身份并制作应该分开的对象的副本(这是 的主要目的之一is:检测和处理别名)。例如:

>>> first_object = [1, 2, 3]
>>> aliased_object = first_object
>>> first_object is aliased_object
True
>>> aliased_object[0]= "this affects first_object"
>>> first_object
['this affects first_object', 2, 3]
>>> copied_object= first_object.copy() #there are other ways to do this, such as slice notation or the copy module, but this is the most simple and direct
>>> first_object is copied_object
False
>>> copied_object[2] = "this DOES NOT affect first_object"
>>> first_object
['this affects first_object', 2, 3]
>>> copied_object
['this affects first_object', 2, "this DOES NOT affect first_object"]

有很多情况会导致 2 个引用被别名,但是在赋值运算符之外(它总是创建对分配对象的引用,如上所述),其中许多依赖于确切的实现,例如,不是 Python 的每个实现将在相同情况下实习字符串或抢先缓存(我不确定在这种情况下正确的术语是什么)相同的整数范围。例如,我安装的 cpython 似乎在启动时缓存了 -8,而本文似乎暗示这超出了正常范围。因此,即使is看起来在您的开发环境中工作,最好站在同一边,完全避免不一致的行为,并使用==. is应该保留用于您实际想要比较身份的情况。

于 2013-01-04T18:40:44.677 回答