两个Python对象如何具有相同的id,但是'is'运算符返回False?

Tha*_*han 2 python object lifetime

id(t+t), id(t*2)
(42838592, 42838592)

(t+t) is (t*2)
False
Run Code Online (Sandbox Code Playgroud)

如果两个变量指向同一个对象'是',则运算符将返回true.但是第一行表示两者具有相同的id但是'is'运算符给出false值.

Ned*_*der 6

在第一个示例中,您的对象不会在时间上重叠:创建一个然后销毁,然后创建另一个具有相同ID的对象.

当你比较它们时is,你会抓住两个对象,所以它们得到不同的id.

  • @CIsForCookies 不,逗号创建了一个元组,其中包含 `id` 返回的两个整数,但不包含作为参数传递给 `id` 的整数——那些已经超出范围并被销毁。详情见[我的回答](/sf/answers/3562683281/)。 (2认同)

aba*_*ert 5

正如Ned Batchelder 的回答函数文档所解释的id

生命周期不重叠的两个对象可能具有相同的id()值。

并且这两个对象具有不重叠的生命周期。

它们是同一个元组表达式的一部分这一事实并没有改变这一点,因为它不是t+t并且t*2那是元组的一部分,它是id(t+t)and id(t*2)。因此,由 返回的这两个整数值id具有重叠的生命周期,但传递给的参数id没有。

理解这一点的一种方法是查看 CPython 如何编译代码:

>>> dis.dis('id(t+t), id(t*2)')
  1           0 LOAD_NAME                0 (id)
              2 LOAD_NAME                1 (t)
              4 LOAD_NAME                1 (t)
              6 BINARY_ADD
              8 CALL_FUNCTION            1
             10 LOAD_NAME                0 (id)
             12 LOAD_NAME                1 (t)
             14 LOAD_CONST               0 (2)
             16 BINARY_MULTIPLY
             18 CALL_FUNCTION            1
             20 BUILD_TUPLE              2
             22 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)

所以,这就是发生的事情。(我将为 选择一个值t,比如说1000,只是为了让这更容易理解。)

  • id, 1000, 并被1000压入堆栈。
  • BINARY_ADD2000在 location 处创建一个值42838592
  • id在该值上被调用并42838592在位置返回一个值,例如,42838616
  • 由于该42838592值不再在堆栈上并且未存储在任何地方,因此id的参数是对它的唯一引用,因此当它在函数末尾被取消时,它会立即被删除。
  • id, 1000, 并被2压入堆栈。
  • BINARY_MULTIPLY创建一个新2000对象。由于位置42838592刚刚返回到对象池,新值会重用该位置。
  • id返回另一个42838592,这次是在位置,比如说,42838640

因此,这两个int42835924283592具有重叠的生命周期(第一个与第二个重叠2000),两个2000s 不重叠。


最后,请注意,如果t是一个小数,

>>> t = 2
>>> (t+t) is (t*2)
True
Run Code Online (Sandbox Code Playgroud)

... 因为所有4值(除了在不寻常的情况下)都是对同一个对象的引用。

同时,如果t是一个常量而不是一个变量,1000+1000 is 1000*2可能是也可能不是真的,这取决于你的 CPython 版本,因为编译单元中常量折叠的工作方式。

所有这些都表明,试图真正利用两个相等的ints 是否是同一个对象几乎总是一个糟糕的主意。您应该关心这个问题的唯一原因是您是否正在尝试更多地了解 CPython 的内部结构。


当然,这都是 CPython 特有的。大多数其他 Python 解释器使用某种形式的垃圾收集器而不是引用计数,因此第一个2000不太可能在创建第二个之前被销毁。另外,并非所有人都使用像 CPython 这样的对象池。更不用说,id只要它们可以保证非重叠对象的唯一值,它们就可以做完全不同的事情。

PyPy 实际上通常会在这里返回相同的值——但这只是因为它首先折叠t+tt*2放入同一个对象;尝试使用t+t和 t*3 and you'll get completely differentid`s。