【问题标题】:id() vs `is` operator. Is it safe to compare `id`s? Does the same `id` mean the same object?id() 与 `is` 运算符。比较`id`s是否安全?相同的“id”是否意味着相同的对象?
【发布时间】:2019-02-15 12:15:57
【问题描述】:

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

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

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

【问题讨论】:

  • (something is something) == (id(something) is id(something)) 返回True
  • @U9-Forward 不一定,如果评估 something 每次都返回一个新对象。这就是造成混乱的全部原因。
  • 是的,没错
  • 这非常需要实际的例子:1)一些未命名的表达式,例如(t+t), (t+2) 2) 一些绑定和未绑定的方法对象:a.method1, a.method2, b.method1... 3) 字符串表达式,例如s = "food" s1 = s[0:3] s2 = s[0:3] 。否则它将完全超出新用户的头脑。他们甚至不理解这些术语以及它们与代码的关系。
  • @smci 好的,我终于可以想出一些说明性的例子,这些例子不会在混合中添加不相关的概念。

标签: python identity identity-operator


【解决方案1】:

根据id() documentationid 只保证是唯一的

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

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

这正是is 所做的——这使得比较ids 变得多余。如果您出于某种原因不能使用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并且之后创建的任何对象都可以占用它的@ 987654338@.

    • 即使在同一代码行中也是如此。例如。 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 integerssome strings。所以即使你从不同的地方得到它们,它们的ids 也可能是一致的。

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

这也没什么大不了的,因为两个变量是否指向同一个对象只有知道对象是否可变才是实际的:if two variables point to the same mutable object, mutating one will (unexpectedly) change the other, too。不可变类型没有这个问题,所以对于它们来说,两个变量指向两个相同的对象或同一个对象都没有关系。


1有时,这被称为“未命名的表达式”。

【讨论】:

    猜你喜欢
    • 2017-02-09
    • 1970-01-01
    • 1970-01-01
    • 2015-12-15
    • 2015-12-18
    • 2020-12-16
    • 1970-01-01
    • 1970-01-01
    • 2015-03-12
    相关资源
    最近更新 更多