为什么创建多个对象而不命名会导致它们在 Python 中具有相同的 id?

2024-11-28 08:37:00
admin
原创
181
摘要:问题描述:我正在用 Python (3.3.3) 做一些事情,我遇到了一些令我困惑的事情,因为据我了解,每次调用类时都会获得一个新的 id。假设你在某个 .py 文件中有这个:class someClass: pass print(someClass()) print(someClass()) 上面的代码返...

问题描述:

我正在用 Python (3.3.3) 做一些事情,我遇到了一些令我困惑的事情,因为据我了解,每次调用类时都会获得一个新的 id。

假设你在某个 .py 文件中有这个:

class someClass: pass

print(someClass())
print(someClass())

上面的代码返回了相同的 id,这让我很困惑,因为我正在调用它,所以它不应该是相同的,对吧?当连续两次调用同一个类时,Python 的工作方式是这样吗?当我等待几秒钟时,它会给出一个不同的 id,但如果我像上面的例子一样同时执行此操作,它似乎不会那样工作,这让我很困惑。

>>> print(someClass());print(someClass())
<__main__.someClass object at 0x0000000002D96F98>
<__main__.someClass object at 0x0000000002D96F98>

它返回相同的内容,但为什么呢?例如,我还注意到了范围

for i in range(10):
    print(someClass())

当快速调用类时,Python 这样做有什么特殊原因吗?我甚至不知道 Python 会这样做,或者这可能是一个错误?如果这不是一个错误,有人可以向我解释如何修复它或一种方法,以便每次调用方法/类时都会生成不同的 id?我很困惑它是如何做到的,因为如果我等待,它确实会改变,但如果我尝试调用同一个类两次或更多次,它就不会改变。


解决方案 1:

对象的仅保证在该对象的生命周期内id是唯一的,而不是在程序的整个生命周期内。您创建的两个对象仅在调用 的持续时间内存在- 之后,它们可供垃圾收集(并且在 CPython 中,立即释放)。由于它们的生命周期不重叠,因此它们共享一个 id 是有效的。someClass`print`

在这种情况下,这也不足为奇,因为结合了两个 CPython 实现细节:首先,它通过引用计数进行垃圾收集(使用一些额外的魔法来避免循环引用问题),其次,id对象的与变量的底层指针的值(即其内存位置)相关。因此,第一个对象(即最近分配的对象)会立即被释放 -下一个分配的对象最终会出现在同一位置也就不足为奇了(尽管这可能还取决于解释器的编译细节)。

如果您依赖具有不同ids 的多个对象,您可能会将它们保留在列表中,以便它们的生命周期重叠。否则,您可能会实现具有不同保证的特定于类的 id - 例如:

class SomeClass:
    next_id = 0

    def __init__(self):
         self.id = SomeClass.nextid
         SomeClass.nextid += 1

解决方案 2:

如果你阅读的文档id,它说:

返回对象的“标识”。这是一个整数,保证在该对象的生命周期内唯一且恒定。两个生命周期不重叠的对象可能具有相同的id()值。

这正是正在发生的事情:您有两个生命周期不重叠的对象,因为第一个对象在第二个对象被创建之前就已经超出了范围。


但不要相信这种情况总是会发生。特别是当你需要处理其他 Python 实现或更复杂的类时。语言所说的只是这两个对象可能具有相同的id()值,而不是它们会具有相同的值。而它们确实会具有相同的值取决于两个实现细节:

  • 垃圾收集器必须在代码开始分配第二个对象之前清理第一个对象 - 这在 CPython 或任何其他引用计数实现中肯定会发生(当没有循环引用时),但对于 Jython 或 IronPython 中的分代垃圾收集器来说,这种情况不太可能发生。

  • 底层的分配器必须非常倾向于重用最近释放的相同类型的对象。这在 CPython 中是真的,它在基本 C 之上有多层花哨的分配器malloc,但大多数其他实现将更多的工作留给了底层虚拟机。


最后一件事: 恰巧object.__repr__包含一个恰巧与十六进制数相同的子字符串,id这只是 CPython 的一个实现工件,在任何地方都无法保证。根据文档:

如果可能的话,这应该看起来像一个有效的 Python 表达式,可用于重新创建具有相同值的对象(给定适当的环境)。如果不可能,<...some useful description…>则应返回以下形式的字符串。

CPythonobject碰巧会 put hex(id(self))(实际上,我相信它相当于sprintf通过 -ing 其指针%p,但由于 CPythonid只是返回相同的指针,将其转换为long最终相同的指针)这一事实在任何地方都无法保证。即使它自……以来一直如此,object甚至在 2.x 早期就存在了。您可以放心地依靠它在交互式提示符下进行这种简单的“这里发生了什么”调试,但不要尝试超出此范围使用它。

解决方案 3:

我感觉到这里有一个更深层次的问题。你不应该依赖于id在程序的整个生命周期内跟踪唯一实例。你应该只是将其视为每个对象实例持续时间内不保证的内存位置指示器。如果你立即创建和释放实例,那么你很可能在同一内存位置创建连续的实例。

也许您需要做的是跟踪一个类静态计数器,该计数器为每个新实例分配一个唯一的 ID,并为下一个实例增加类静态计数器。

解决方案 4:

由于没有被保留,因此它释放了第一个实例,然后由于在此期间内存没有发生任何事情,因此它会在同一位置第二次实例化。

解决方案 5:

尝试一下这个,尝试调用以下命令:

a = someClass()
for i in range(0,44):
    print(someClass())
print(a)

你会看到一些不同的东西。为什么?因为“foo”循环中第一个对象释放的内存被重用了。另一方面,a由于它被保留了,所以不会被重用。

解决方案 6:

内存位置(和 id)未被释放的一个例子是:

print([someClass() for i in range(10)])

现在所有 ID 都是唯一的。

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1259  
  IPD(Integrated Product Development)流程管理作为一种先进的产品开发管理理念和方法,在提升企业创新能力方面发挥着至关重要的作用。它打破了传统产品开发过程中部门之间的壁垒,通过整合资源、优化流程,实现产品的快速、高效开发,为企业在激烈的市场竞争中赢得优势。IPD流程管理的核心概念IPD流程...
IPD流程中PDCP是什么意思   11  
  IPD(Integrated Product Development)流程管理作为一种先进的产品开发管理模式,旨在通过整合各种资源,实现产品的高效、高质量开发。在这一过程中,团队协作无疑是成功的关键。有效的团队协作能够打破部门壁垒,促进信息共享,提升决策效率,从而确保产品开发项目顺利推进。接下来,我们将深入探讨IPD流...
IPD培训课程   9  
  IPD(Integrated Product Development)研发管理体系作为一种先进的产品开发理念和方法,在众多企业中得到了广泛应用。它旨在打破部门壁垒,整合资源,实现产品开发的高效、协同与创新。在项目周期方面,IPD研发管理体系有着深远且多维度的影响,深入剖析这些影响,对于企业优化产品开发流程、提升市场竞争...
华为IPD流程   11  
  IPD(Integrated Product Development)流程管理是一种先进的产品开发管理模式,旨在通过整合企业的各种资源,实现产品的高效、高质量开发。它涵盖了从产品概念提出到产品退市的整个生命周期,对企业的发展具有至关重要的意义。接下来将详细阐述IPD流程管理的五个阶段及其重要性。概念阶段概念阶段是IPD...
IPD概念阶段   12  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用