id() 与 is 运算符。比较 `id` 安全吗?相同的 `id` 是否表示相同的对象?

id() vs `is` operator. Is it safe to compare `id`s? Does the same `id` mean the same object?

我可以在多大程度上依赖对象的 id() 及其在实践中的独特性?例如:

(作为对 Canonicals for Python: are objects with the same id() the same object, `is` operator, unbound method objects 的建议规范而写)

根据id() documentation,一个id只能保证是唯一的

  1. 特定对象的生命周期,并且
  2. 在特定的解释器实例中

因此,比较 ids 是不安全的,除非你也以某种方式确保 ids 被采用的两个对象在比较时仍然存在(并且与相同的 Python 解释器实例相关联,但您需要真正尝试使其变为 false)。

这正是 is 所做的——这使得比较 id 变得多余。如果您出于某种原因不能使用 is 语法,那么总是有 operator.is_.


现在,对象在比较时是否仍然存在并不总是显而易见的(有时是非常明显的-明显):

  • 访问一些属性(例如bound methods of an object每次创建一个新对象。所以,每个属性访问的结果 id 可能相同也可能不同。

    示例:

    >>> class C(object): pass
    >>> c=C()
    >>> c.a=1
    
    >>> c.a is c.a
    True        # same object each time
    
    >>> c.__init__ is c.__init__
    False       # a different object each time
    
    # The above two are not the only possible cases.
    # An attribute may be implemented to sometimes return the same object
    # and sometimes a different one:
    @property
    def page(self):
        if check_for_new_version():
            self._page=get_new_version()
        return self._page
    
  • 如果一个对象是作为计算表达式的结果创建的并且没有保存在任何地方,它会立即被丢弃,1 之后创建的任何对象都可以占用其 id.

    • 即使在同一行代码中也是如此。例如。 id(create_foo()) == id(create_bar()) 的结果未定义。

      示例:

      >>> id([])     #the list object is discarded when id() returns
      39733320L
      >>> id([])     #a new, unrelated object is created (and discarded, too)
      39733320L      #its id can happen to be the same
      >>> id([[]])
      39733640L      #or not
      >>> id([])
      39733640L      #you never really know
      

由于比较 ids 时的上述安全要求,保存 id 而不是对象不是很有用,因为无论如何你都必须保存对对象本身的引用 - 到确保它还活着。也没有任何性能提升:is implementation is as simple as comparing pointers.


最后,作为内部优化(和实现细节,因此实现和版本之间可能有所不同),CPython 重用了一些常用的不可变类型的简单对象。在撰写本文时,这包括 small integers and some strings。所以即使你从不同的地方得到它们,它们的 id 也可能重合。

这并没有(在技术上)违反上述 id() 文档的唯一性承诺:重用对象在所有重用过程中保持活动状态。

这也没什么大不了的,因为两个变量是否指向同一个对象只是实际知道对象是否可变:。不可变类型没有这个问题,所以对于它们来说,两个变量指向两个相同的对象还是指向同一个对象都没有关系。


1有时,这叫做"unnamed expression"。