Python documentation for id()
function陈述如下:
This is an integer which is guaranteed to be unique and constant for this object during its lifetime. Two objects with non-overlapping lifetimes may have the same
id()
value.CPython implementation detail: This is the address of the object in memory.
尽管如此,下面的代码片段显示 id
是重复的。由于我没有明确地del
对象,我假设它们都是活的并且是唯一的(我不知道非重叠是什么意思)。
>>> g = [0, 1, 0]
>>> for h in g:
... print(h, id(h))
...
0 10915712
1 10915744
0 10915712
>>> a=0
>>> b=1
>>> c=0
>>> d=[a, b,c]
>>> for e in d:
... print(e, id(e))
...
0 10915712
1 10915744
0 10915712
>>> id(a)
10915712
>>> id(b)
10915744
>>> id(c)
10915712
>>>
不同对象的id
值怎么可能相同呢?是因为值 0
(int
类的对象)是一个常量并且解释器/C 编译器进行了优化吗?
如果我要执行 a = c
,那么我理解 c
与 a
具有相同的 id
> 因为 c
只是对 a
(别名)的引用。我希望对象 a
和 c
具有不同的 id
值,但是,如上所示,它们具有相同的值。
这是怎么回事?还是我看错了?
我希望用户定义类对象的 id
始终是唯一的,即使它们具有完全相同的成员值也是如此。
有人可以解释这种行为吗? (我查看了询问使用 id()
的其他问题,但它们转向了其他方向)
编辑(2019 年 9 月 30 日):
为了扩展我已经写过的内容,我在不同的终端中运行了 python 解释器,并检查了所有终端上的 id
是否为 0
,它们完全相同(对于同一个口译员);不同解释器的多个实例对 0
具有相同的 id
。 Python2 与 Python3 具有不同的值,但相同的 Python2 解释器具有相同的 id
值。
我的问题是因为 id()
的文档没有说明任何此类优化,这似乎具有误导性(我不希望每个怪癖都被注意到,但一些注释与 CPython注意会很好)...
编辑 2(2019 年 9 月 30 日):
问题源于理解这种行为并了解是否有任何钩子(Hook)可以以类似的方式优化用户定义的类(通过修改 __equals__
方法来识别两个对象是否相同;也许将指向内存中的相同地址,即相同的 id
?或者使用一些 metaclass
属性)
最佳答案
ID 保证在对象的生命周期内是唯一的。如果一个对象被删除,一个新的对象可以获得相同的 id。当项目的引用计数降为零时,CPython 将立即删除项目。垃圾收集器只需要打破引用循环。
CPython 还可以缓存和重用某些不可变对象(immutable对象),例如小整数和由作为有效标识符的文字定义的字符串。这是您不应依赖的实现细节。通常认为对此类对象使用 is
检查是不合适的。
此规则有某些异常(exception)情况,例如,使用 is
检查可能 - interned在将它们与普通 ==
运算符进行比较之前,将字符串作为优化是可以的。 dict
内置函数使用此策略进行查找,以加快标识符的查找速度。
a is b or a == b # This is OK
如果字符串恰好是驻留的,那么上面的代码可以通过简单的 id 比较返回 true 而不是较慢的逐字符比较,但当且仅当 a == b
(因为如果 a is b
那么 a == b
也必须为真)。但是,.__eq__()
的良好实现应该已经在内部执行了 is
检查,因此充其量您只能避免调用 .__eq__( )
。
Thanks for the answer, would you elaborate around the uniqueness for user-defined objects, are they always unique?
任何对象 的 ID(无论是否是用户定义的)在对象的生命周期内都是唯一的。区分对象和变量很重要。可以有两个或更多变量引用同一个对象。
>>> a = object()
>>> b = a
>>> c = object()
>>> a is b
True
>>> a is c
False
缓存优化意味着在某些人可能天真地认为应该得到的情况下,您并不总是能保证得到一个新对象,但这无论如何都不会违反 ID 的唯一性保证。 int
和 str
等内置类型可能有一些缓存优化,但它们遵循完全相同的规则:如果它们同时存在,并且它们的 ID 相同,那么它们就是同一个对象。
缓存并不是内置类型所独有的。您可以为自己的对象实现缓存。
>>> def the_one(it=object()):
... return it
...
>>> the_one() is the_one()
True
甚至用户定义的类也可以缓存实例。例如,此类仅创建其自身的一个实例。
>>> class TheOne:
... _the_one = None
... def __new__(cls):
... if not cls._the_one:
... cls._the_one = super().__new__(cls)
... return cls._the_one
...
>>> TheOne() is TheOne() # There can be only one TheOne.
True
>>> id(TheOne()) == id(TheOne()) # This is what an is-check does.
True
请注意,每个构造表达式的计算结果都是一个具有相同 id 的对象。但是这个 id 对对象是唯一的。两个表达式都引用了相同的对象,因此它们当然具有相同的 ID。
上面的类只保留一个实例,但你也可以缓存一些其他的实例。也许最近使用过的实例,或者以您期望常见的方式配置的实例(如 int 所做的),等等。
关于python - 理解 python id() 的唯一性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58177734/