正如Ned Batchelder's answer 和the docs for the id function 所解释的:
生命周期不重叠的两个对象可能具有相同的id() 值。
并且这两个对象的生命周期不重叠。
它们是同一个元组表达式的一部分这一事实并没有改变这一点,因为不是t+t 和t*2 是元组的一部分,而是id(t+t) 和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
所以,这就是发生的事情。 (我将为t 选择一个值,比如1000,只是为了更容易理解。)
-
id、1000 和 1000 被压入堆栈。
-
BINARY_ADD 在位置 42838592 创建一个 2000 值。
-
在该值上调用
id 并在位置返回42838592 值,例如42838616。
- 由于
42838592 值不再在堆栈中并且没有存储在任何地方,id 的参数是对其的唯一引用,因此当它在函数末尾被取消引用时,它会立即被删除.
-
id、1000 和 2 被压入堆栈。
-
BINARY_MULTIPLY 创建一个新的 2000 对象。由于位置 42838592 刚刚返回到对象池,因此新值会重用该位置。
-
id 返回另一个42838592,这次是在某个位置,比如42838640。
因此,int 两个值 4283592 和 4283592 具有重叠的生命周期(第一个与第二个 2000 重叠),两个 2000s 不重叠。
最后,注意如果t 是一个小数字,
>>> t = 2
>>> (t+t) is (t*2)
True
...因为所有4 值(不寻常的情况除外)都是对同一个对象的引用。
同时,如果t 是常量而不是变量,1000+1000 is 1000*2 可能为真,也可能不为真,具体取决于您的 CPython 版本,因为编译单元内的常量折叠的工作方式。
所有这些都表明,试图真正利用两个相等的ints 是否是同一个对象几乎总是一个糟糕的主意。您应该关心这个问题的唯一原因是您是否想了解更多有关 CPython 内部的信息。
当然,这都是 CPython 特有的。大多数其他 Python 解释器使用某种形式的垃圾收集器而不是引用计数,因此第一个 2000 在创建第二个之前不太可能被销毁。另外,并非所有人都使用像 CPython 这样的对象池。更不用说他们可以为id 做完全不同的事情,只要他们能保证非重叠对象的唯一值。
PyPy 实际上通常会在这里返回相同的值——但这只是因为它首先将t+t 和t*2 折叠到同一个对象中;试试t+t 和 t*3and you'll get completely differentid`s。