“a is b”和“id(a) == id(b)”在Python中有什么区别?

43

id()内置函数返回一个整数(或长整数),它保证在它的生命周期内唯一且不变。

相比之下,is运算符提供了对象的标识。

那么为什么可能会有两个具有相同id但在is检查中返回False的对象呢?以下是一个示例:

>>> class Test():
...   def test():
...     pass
>>> a = Test()
>>> b = Test()
>>> id(a.test) == id(b.test)
True
>>> a.test is b.test
False

一个更令人不安的例子:(继续上面的内容)
>>> b = a
>>> b is a
True
>>> b.test is a.test
False
>>> a.test is a.test
False

然而:
>>> new_improved_test_method = lambda: None
>>> a.test = new_improved_test_method
>>> a.test is a.test
True

2
并不是说我真的需要这个用于实际程序或元类绝地武士心灵魔法,你知道的。只是我在火车上无聊,尝试了一下 :) - badp
1
我认为重要的问题是为什么 a.test is a.test 是 False。一旦你知道了这个,剩下的就会很清楚了... - Skilldrick
石油泄漏很好,它即将来到你附近的城市!也许你应该组织一些聚会来庆祝它的到来! - badp
== 和 is 是完全独立的。这里有一个情况,is 不意味着相等。a = float('nan'); print a is a, a == a - user97370
2
@Paul: 我不是在问 a == b,我是在问 id(a) == id(b) :) - badp
1个回答

66
>>> b.test is a.test
False
>>> a.test is a.test
False

每次查找方法时都会动态创建该方法。函数对象(始终是同一个对象)实现了描述符协议,其__get__方法创建绑定的方法对象。通常情况下,没有两个绑定的方法对象是相同的。

>>> id(a.test) == id(b.test)
True
>>> a.test is b.test
False

这个例子是具有欺骗性的。第一个结果仅仅是因为巧合而为Truea.test创建了一个绑定方法,在计算id(a.test)后被垃圾回收,因为没有任何引用指向它。(请注意你引用的文档中说id在对象生命周期内是"唯一且恒定的"(强调为我的)。)b.test碰巧与之前相同的绑定方法id,并且允许这样做,因为现在没有其他对象具有相同的id。

请注意,您应该很少使用is,甚至更少使用idid(foo) == id(bar)总是错误的。


关于你的新例子,希望你现在明白它的作用:

>>> new_improved_test_method = lambda: None
>>> a.test = new_improved_test_method
>>> a.test is a.test
True
在这种情况下,我们不会从类的函数自动绑定 self 并返回绑定方法对象来动态创建方法。在这种情况下,你只需要将一个函数作为实例属性进行存储。查找时没有特殊操作发生(描述符仅在查找类属性时被调用),因此每次查找该属性时,你会得到最初存储的原始对象。

10
每次查找方法时都会即时创建方法。函数对象(始终是同一个对象)实现了描述符协议,并且其__get__方法会创建绑定的方法对象。通常情况下,不会有两个绑定的方法是同一个对象。 哈,这对我来说是好消息。很好! - Will McCutchen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接