关于python:id()vs`is`运算符。

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

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

  • id(a) == id(b)是指a is b还是相反?相反呢?
  • 把一个id保存到以后使用的地方(例如保存到某个注册表而不是对象本身)有多安全?

(作为响应Python规范的建议规范编写:are objects with the same id()the same object,`is` operator,unbound method objects)


根据id()文件,只有保证id是唯一的

  • 在特定对象的生命周期内,以及
  • 在特定的解释器实例中
  • 因此,比较id是不安全的,除非您还以某种方式确保在比较时,取id的两个对象仍然是活动的(并且与同一个python解释器实例相关联,但您需要真正尝试使其变为假)。

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

    现在,一个对象在比较时是否还活着并不总是显而易见的(有时是非常不明显的):

    • 每次访问一些属性(例如对象的绑定方法)都会创建一个新对象。因此,结果的id在每个属性访问上可能相同,也可能不同。

      例子:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      >>> 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())的结果未定义。

        例子:

        1
        2
        3
        4
        5
        6
        7
        8
        >>> 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

    在比较id时,由于上述安全要求,保存id而不是对象并不是很有用,因为无论如何都必须保存对对象本身的引用,以确保对象保持活动。没有任何性能提高:is实现与比较指针一样简单。

    最后,作为一个内部优化(和实现细节,因此在实现和发布之间可能有所不同),CPython重用了一些经常使用的不可变类型的简单对象。在写这篇文章时,它包括小整数和一些字符串。所以,即使你从不同的地方得到它们,它们的id也可能是一致的。

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

    这也没什么大不了的,因为两个变量是否指向同一个对象才是了解对象是否可变的实用方法:如果两个变量指向同一个可变对象,则一个变量的变化(意外)也会改变另一个变量。不可变类型没有这个问题,所以对于它们来说,两个变量指向两个相同的对象或同一个对象并不重要。

    有时,这称为"未命名表达式"。