python - 理解 python id() 的唯一性

标签 python python-3.x

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,那么我理解 ca 具有相同的 id > 因为 c 只是对 a(别名)的引用。我希望对象 ac 具有不同的 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 的唯一性保证。 intstr 等内置类型可能有一些缓存优化,但它们遵循完全相同的规则:如果它们同时存在,并且它们的 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/

相关文章:

python - pandas 中的关键错误

python - ssl.SSLError : [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl. c:749)

python - 如何在不复制所有内容的情况下将 Sphinx 与子包一起使用?

python - QGraphicsItem 移动后没有留在原处

python - 如何在直方图上设置轴并交换 x 轴和 y 轴?

python - FilePath 宏不包含 Pycharm 中的值

python - 在 Arch Linux (ARM) 上安装 python2 pandas 模块

python - 无法使用我的 scraper 中定义的 xpath 获取项目

python-3.x - python-twitter异常处理

python - DataFrame GroupBy 多级选择