【问题标题】:Unexpected id method behaviour [duplicate]意外的id方法行为[重复]
【发布时间】:2019-11-09 13:30:27
【问题描述】:

简单来说,为什么这段代码返回 False 而不是 true:

a = 10
print(id(a) is id(a)) # why false?

id() 返回整数,相同的整数变量指向相同的整数对象。为什么它返回false呢?有什么区别:

a = 10
b = 10
print(a is b) # it returns True

感谢您的解释。

【问题讨论】:

  • “和相同的整数变量指向相同的整数对象”——是什么让你这么认为?想一想:这将如何在 Python 解释器中有效地实现?它可能需要缓存数十亿个对象……代价是千兆字节的内存。

标签: python low-level


【解决方案1】:

因为,虽然

a is a

id(a) 是一个大整数,不与is 比较

>>> a = 10
>>> print(id(a) is id(a))
False
>>> print(id(a),id(a))
(44337068, 44337068)
>>>

检查https://docs.python.org/2/c-api/int.html#c.PyInt_FromLongis 比较的整数 - 但请记住这是一个实现细节,因此不要依赖它(始终将整数与== 进行比较):

>>> a = 256
>>> b = 256
>>> a is b
True
>>> a = 257
>>> b = 257
>>> a is b
False

(来自"is" operator behaves unexpectedly with integers

为了进一步激励您始终使用==,请执行以下操作:

a = 300
b = 300
print(a is b)
print(a is 300)
def c(a): return a is 300
def d(a): return a is b
print(c(a))
print(d(a))

保存到文件并运行时,会打印出TrueTrueFalseTrue...

【讨论】:

  • 难道不是因为 is 运算符检查它们是否引用了相同的对象而不是相等吗?因为 44337068 是 44337068 是 True。
  • @dan-klasson 是的,但是相同对象仅适用于 Python 中的小整数(因为它们已被缓存)。正如您所展示的,对于常量(在同一表达式中)也是如此,但是 44337068 is 44337067+1 是 False。
  • 有趣的是,如果您使用的是 python 控制台:>>> a = 300 >>> b = 300 >>> a is b >>> False 但是如果您使用任何 IDE(pycharm,vsc)使用与解释器相同的 python 二进制文件运行它,您会得到 True。这是为什么呢?
  • @PawełBąkiewicz “因为”is 没有定义为返回整数相等,如果你尝试,你一定会得到令人惊讶的结果。我用更多例子扩展了我的答案。
【解决方案2】:

例如,如果您尝试这样做

 print(str(id(id(a))) + " " + str(id(id(a))))
In [54]: print(str(id(id(a))) + " " + str(id(id(a))))
2460780951888 2460780951888

In [55]: print(str(id(id(a))) + " " + str(id(id(a))))
2460780951472 2460780951472

In [56]: print(str(id(id(a))) + " " + str(id(id(a))))
2460782062320 2460782062320

In [57]: print(str(id(id(a))) + " " + str(id(id(a))))
2460780951888 2460780951888

In [58]: print(str(id(id(a))) + " " + str(id(id(a))))
2460782473392 2460782475664

每次都会为大整数返回一个新的 id

正如@thebjorn 提到的,python 解释器不会缓存大整数,只有 -5 到 256 之间的整数(在 CPython 实现中)始终保持活动状态。

每次引用一个大整数时,都会为其创建一个新对象,而对于一个小整数,会继续使用相同的对象(作为一种优化),但这并不保证在所有 python 实现中都保持正确。

【讨论】: