为何在CPython中id({}) == id({})且id([]) == id([])?
为什么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
2 个回答
45
当你调用 id({})
时,Python 会创建一个字典(dict),然后把它传给 id
函数。这个 id
函数会获取这个字典的唯一标识(其实就是它在内存中的位置),然后就把这个字典丢掉了。这样一来,这个字典就被销毁了。如果你在很短的时间内连续调用两次这个函数(中间没有创建其他字典),Python 第二次创建的字典可能会使用和第一次相同的内存位置。(CPython 的内存分配器让这种情况发生的可能性比你想象的要高。)因为在 CPython 中,id
是用内存位置作为对象的标识,所以这两个对象的 id 就是一样的。如果你把字典赋值给一个变量,然后再获取它的 id()
,就不会出现这种情况,因为这两个字典是同时存在的,所以它们的 id
必然不同。
可变性在这里并不是直接相关的,但代码对象缓存的元组和字符串是有关系的。在同一个代码对象(比如函数、类体或模块体)中,相同的字面量(整数、字符串和某些元组)会被重复使用。可变对象是不能被重复使用的,它们总是在运行时创建。
简单来说,一个对象的 id 只有在对象存在的期间是唯一的。对象被销毁后,或者在它被创建之前,其他东西可能会使用相同的 id。
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()))