为什么CPython中的id({})== id({})和id([])== id([])?

spe*_*hil 26 python identity cpython python-internals

为什么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([]))
Run Code Online (Sandbox Code Playgroud)

我有一些想法可能,但找不到具体原因.

编辑

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

[1] id([])
4330909912
[2] x = []
[3] id(x)
4330909912
[4] id([])
4334243440
Run Code Online (Sandbox Code Playgroud)

Tho*_*ers 43

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

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

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

  • @Glenn:我不确定为什么我的答案比你的答案少.当然,它更长,它不包含你在你的实验,但那是因为我实际上从CPython源知道这些东西.没有必要进行实验. (5认同)

Gle*_*ard 37

CPython一旦超出范围就会收集垃圾,所以第二个[]是在第一个[]收集后创建的.所以,大多数时候它最终都在同一个内存位置.

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

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())
Run Code Online (Sandbox Code Playgroud)

  • @Laurence:不,不一定.分配器复杂且经过大量优化; 他们不只是采取第一个地址.在这种情况下,dict对象和列表对象具有非常不同的大小,这可能将它们放入不同的分配桶中. (2认同)