hex(id()) returns 同一对象的交替数字,但 id() returns 固定数字

hex(id()) returns alternating numbers for the same object, but id() returns a fixed number

Python版本:3.6.9

我发现了一件奇怪的事:

演示

class A:
    pass

print("{:#^30s}".format("hex"))
print(hex(id(A.__dict__)))
print(hex(id(A.__dict__)))
print(hex(id(A.__dict__)))
print(hex(id(A.__dict__)))
print()

print("{:#^30s}".format("decimal"))
print(id(A.__dict__))
print(id(A.__dict__))
print(id(A.__dict__))
print(id(A.__dict__))
print()

print("{:#^30s}".format("both"))
print(id(A.__dict__), hex(id(A.__dict__)))
print(id(A.__dict__), hex(id(A.__dict__)))
print(id(A.__dict__), hex(id(A.__dict__)))
print(id(A.__dict__), hex(id(A.__dict__)))

输出

#############hex##############
0x7f4c06312558   <--- one
0x7f4c06312cd8   <--- two
0x7f4c06312558   <--- one
0x7f4c06312cd8   <--- two

###########decimal############
139964498126168   <--- all the same
139964498126168
139964498126168
139964498126168

#############both#############
139964498126168 0x7f4c06312558   <--- all the same
139964498126168 0x7f4c06312558
139964498126168 0x7f4c06312558
139964498126168 0x7f4c06312558

据我所知,A 是一个类型对象,它是在源代码编译阶段立即创建的。它有一些属性,__dict__ 是其中之一,它应该是同一个对象(mappingproxy),而 A 对象的整个生命周期。所以它的 id 也应该固定,并且 hex(id(A.__dict__)) 应该每次都 return 相同的数字。但是我在 hex() 案例中看到了其他行为。更奇怪的是,当 id() 在附近时,这种行为就会消失。

这里发生了什么?

类似问题:Python reference to an new instance alternating

类型 __dict__ 的基础 dict 没有改变,但 __dict__ 本身 is implemented much like a C-level @property, where the implementing function produces a new mappingproxy every time you access it(调用 PyDictProxy_New(type->tp_dict);,其中 tp_dict 是实际的底层 dict 指针)。

the other question 非常相似,这里的问题是一个对象被重复创建,id()hex 的精确分配模式改变了分配器中的分配交错,因此您不要总是找回相同的记忆。

这可能看起来效率低下,但在实践中并不重要。 class 的大部分使用隐式和直接使用 tp_dict,因此不会创建映射代理。在 Python 层实际访问 __dict__ 是相当罕见的,因此很少支付重新包装的开销。