关于python:为什么在CPython中id({})== id({})和id([])== id([])?

Why does id({}) == id({}) and id([]) == id([]) in CPython?

为什么CPython(不了解其他Python实现)具有以下行为?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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([]))

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

编辑

为了进一步证明格伦和托马斯的观点:

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


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

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

简而言之,对象的ID仅在对象的生存期内是唯一的。在销毁对象之后或在创建对象之前,其他对象可以具有相同的ID。


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

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

1
2
3
4
5
6
7
8
class A(object):
    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())


它在Jython中的工作方式不同...

1
2
3
4
>>> id({})
1
>>> id([])
2

是否可以对"内装"常用(即空)容器进行优化以节省分配成本?

(在CPython中)这建议不:

1
2
3
4
5
6
7
8
9
10
11
>>> def mutateid(obj):
...   obj.append('x')
...   print obj
...   print id(obj)
...
>>> mutateid([])
['x']
4299590472
>>> id([])
4299590472
>>>


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

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