28

为什么 CPython(不知道其他 Python 实现)有以下行为?

tuple1 = ()
tuple2 = ()                                                                                                   
dict1 = {}
dict2 = {}
list1 = []
list2 = []
# makes sense, tuples are immutable
assert(id(tuple1) == id(tuple2))
# also makes sense dicts are mutable
assert(id(dict1) != id(dict2))
# lists are mutable too
assert(id(list1) != id(list2))
assert(id(()) == id(()))
# why no assertion error on this?
assert(id({}) == id({}))
# or this?
assert(id([]) == id([]))

我有一些想法为什么这样,但找不到具体的原因。

编辑

为了进一步证明 Glenn 和 Thomas 的观点:

[1] id([])
4330909912
[2] x = []
[3] id(x)
4330909912
[4] id([])
4334243440
4

3 回答 3

45

当您调用 时id({}),Python 会创建一个 dict 并将其传递给id函数。该id函数获取它的 id(它的内存位置),并丢弃 dict。字典被破坏了。当您快速连续执行两次(同时没有创建任何其他字典)时,Python 第二次创建的字典恰好使用与第一次相同的内存块。(CPython 的内存分配器比听起来更有可能。)因为(在 CPython 中)id使用内存位置作为对象 id,所以两个对象的 id 是相同的。如果您将 dict 分配给一个变量然后获取它,这显然不会发生id(),因为 dicts 同时处于活动状态,因此它们id必须不同。

可变性不会直接发挥作用,但缓存元组和字符串的代码对象会发挥作用。在相同的代码对象(函数或类主体或模块主体)中,将重复使用相同的文字(整数、字符串和某些元组)。可变对象永远不能重复使用,它们总是在运行时创建。

简而言之,对象的 id 仅在对象的生命周期内是唯一的。在对象被销毁之后,或者在它被创建之前,其他东西可以具有相同的 id。

于 2010-10-06T22:08:56.623 回答
42

一旦对象超出范围,CPython 就会对其进行垃圾收集,因此第二个[]对象是在第一个[]对象被收集后创建的。因此,大多数情况下它最终都位于相同的内存位置。

这非常清楚地显示了正在发生的事情(在 Python 的其他实现中输出可能会有所不同):

class A:
    def __init__(self): print("a")
    def __del__(self): print("b")

# a a b b False
print(A() is A())
# a b a b True
print(id(A()) == id(A()))
于 2010-10-06T22:08:54.843 回答
-6

列表和字典上的 == 运算符不会比较对象 ID 以查看它们是否是同一个对象 -obj1 is obj2用于此目的。

相反, == 运算符比较 dict 列表的成员以查看它们是否相同。

于 2010-10-06T22:08:34.567 回答