【问题标题】:How can two Python objects have same id but 'is' operator returns False?两个 Python 对象如何具有相同的 id 但 'is' 运算符返回 False?
【发布时间】:2018-06-17 02:47:42
【问题描述】:
id(t+t), id(t*2)
(42838592, 42838592)

(t+t) is (t*2)
False

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

【问题讨论】:

  • 尝试存储值:t2a, t2b = t+t, t*2。那么它们将不会有相同的id(当然,由于 small-int 优化或编译器常量折叠,它们是同一个对象时除外……)
  • 无论如何,这是由id 函数记录的。
  • 您要比较 ID 的对象与您要比较 is 的对象不同(而且它们彼此也不相同)。跨度>
  • @abarnert:这不是“解释”的。 “id 保证在这个对象的生命周期内是唯一且恒定的。具有非重叠生命周期的两个对象可能具有相同的 id() 值。” 除非我们都了解哪种类型,否则不会告诉我们太多信息Python 对象 t+tobj.meths[0:3] 是,并且知道每个对象的生命周期。 id() 文档对其中任何一个都没有说明。

标签: python object lifetime


【解决方案1】:

在第一个示例中,您的对象不会在时间上重叠:创建一个然后销毁,然后使用相同的 id 创建另一个。

当您将它们与 is 进行比较时,您会同时持有这两个对象,因此它们会获得不同的 id。

【讨论】:

  • 逗号不会创建一个包含两个对象的元组吗?
  • @CIsForCookies 不,逗号创建一个元组,其中包含 id 返回的两个整数,但不包含作为参数传递给 id 的整数——那些已经超出范围并被被摧毁。详情请见my answer
【解决方案2】:

正如Ned Batchelder's answerthe docs for the id function 所解释的:

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

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

它们是同一个元组表达式的一部分这一事实并没有改变这一点,因为不是t+tt*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,只是为了更容易理解。)

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

因此,int 两个值 42835924283592 具有重叠的生命周期(第一个与第二个 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+tt*2 折叠到同一个对象中;试试t+t 和 t*3and you'll get completely differentid`s。

【讨论】:

    猜你喜欢
    • 2019-09-03
    • 1970-01-01
    • 2022-01-23
    • 1970-01-01
    • 2017-02-09
    • 2015-11-16
    • 1970-01-01
    • 2013-11-22
    • 2020-11-29
    相关资源
    最近更新 更多