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

2024-11-28 08:37:00
admin
原创
10
摘要:问题描述:我正在用 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 都是唯一的。

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   649  
  如何借鉴华为IPD体系优化企业研发?在当今竞争激烈的市场环境中,企业要想保持技术领先和产品竞争力,必须拥有一套高效且严谨的研发管理体系。华为作为全球领先的ICT解决方案提供商,其集成产品开发(IPD, Integrated Product Development)体系与质量管理体系(如ISO 9000系列)的融合实践,...
IPD项目管理   0  
  IPD流程图的7种经典绘制方法详解在产品开发领域,集成产品开发(Integrated Product Development,简称IPD)流程被广泛应用,以提高产品开发的效率和质量。IPD流程图作为这一流程的可视化工具,其绘制方法至关重要。本文将详细介绍七种经典的IPD流程图绘制方法,帮助项目管理人员和团队更好地理解和...
IPD研发管理体系   0  
  IPD流程:企业创新管理的核心引擎在当今快速变化的市场环境中,企业要想持续保持竞争力,就必须不断进行创新。而IPD(Integrated Product Development,集成产品开发)流程作为一种先进的产品开发管理模式,正逐渐成为众多企业提升创新能力、加速产品上市速度、降低开发成本的重要选择。本文将深入探讨IP...
IPD管理   0  
  IPD流程与传统产品开发流程的概述在产品开发领域,企业不断寻求高效、系统的管理方法以确保产品能够顺利从概念转化为市场成功的产品。集成产品开发(Integrated Product Development,简称IPD)流程与传统产品开发流程是两种截然不同的管理理念和方法。传统产品开发流程往往以职能部门为核心,各部门按顺序...
IPD流程中PDCP是什么意思   0  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

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

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

内置subversion和git源码管理

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

免费试用