tl; dr Python重用ID吗?两个具有非重叠生命期的对象获得相同ID的可能性有多大?
背景:我一直致力于一个复杂的项目,纯粹用Python 3编写。我一直在看测试中的一些问题,并花了很多时间寻找根本原因。经过一些分析后,我怀疑当测试作为一个整体运行时(它由一个专门的调度程序编排并运行),它会重用一些模拟方法,而不是用原始方法来设置新对象。为了检查翻译是否重用,我使用了id()
。
问题:id()
通常工作并显示对象标识符,让我告诉我的电话是在创建新实例而不是重用。但是如果两个对象相同的话,会发生什么? The documentation说:
返回对象的“标识”。这是一个整数,在该生命周期内保证该对象是唯一且恒定的。具有非重叠寿命的两个对象可具有相同的
id()
值。
问题:
id()
值?是在它随机选择相同的内存区域时吗?如果它只是随机的,似乎极不可能,但它仍然无法保证。id()
,但是这个方法得到了与它被模拟时相同的id,它实际上只是一个模拟。是的,CPython重新使用id()
值。不要指望这些在Python程序中是唯一的。
返回对象的“标识”。这是一个整数,在该生命周期内保证该对象是唯一且恒定的。具有非重叠生存期的两个对象可以具有相同的id()值。
大胆强调我的。只要对象处于活动状态,id就是唯一的。从内存中删除没有引用的对象,允许将id()
值重新用于另一个对象,因此不重叠的生命周期措辞。
请注意,这仅适用于CPython,python.org提供的标准实现。还有其他Python实现,例如IronPython,Jython和PyPy,它们可以自己选择如何实现id()
,因为它们每个都可以在如何处理内存和对象生存期方面做出明确的选择。
解决您的具体问题:
id()
是内存地址。新对象将插入下一个可用内存空间,因此如果特定内存地址有足够的空间来容纳下一个新对象,则将重用内存地址。在创建大小相同的新对象时,您可以在解释器中看到这个:
>>> id(1234)
4546982768
>>> id(4321)
4546982768
1234
文字创建一个新的整数对象,id()
为其生成一个数值。由于没有对int
值的进一步引用,它将再次从内存中删除。但是使用不同的整数文字再次执行相同的表达式,并且你可能会看到相同的id()
值(垃圾收集运行打破循环引用可以释放更多内存,所以你也可能再也看不到相同的id()
。
所以它不是随机的,但在CPython中它是内存分配算法的一个功能。weakref
weak reference。
例如,首先记录对象引用,然后再检查它:
import weakref
# record
object_ref = weakref.ref(some_object)
# check if it's the same object still
some_other_reference is object_ref() # only true if they are the same object
弱引用不会保持对象存活,但如果它存活,那么object_ref()
将返回它(否则它将返回None
)。
您可以使用这种机制生成真正唯一的标识符,请参阅下文。gc.get_referrers()
function引用给定对象,但请注意,它不会为您提供变量名称。它为您提供了对象,例如字典对象,它是将对象作为全局引用的模块的__dict__
属性等。对于完全在您控制之下的代码,最多使用gc.get_referrers()
作为工具来提醒自己对象的位置是什么在编写代码时引用它来删除它们。如果必须在Python应用程序的生命周期中具有唯一标识符,则必须实现自己的工具。如果您的对象是可清除的并且支持弱引用,那么您可以使用WeakKeyDictionary
instance将任意对象与UUIDs相关联:
from weakref import WeakKeyDictionary
from collections import defaultdict
from uuid import uuid4
class UniqueIdMap(WeakKeyDictionary):
def __init__(self, dict=None):
super().__init__(self)
# replace data with a defaultdict to generate uuids
self.data = defaultdict(uuid4)
if dict is not None:
self.update(dict)
uniqueidmap = UniqueIdMap()
def uniqueid(obj):
"""Produce a unique integer id for the object.
Object must me *hashable*. Id is a UUID and should be unique
across Python invocations.
"""
return uniqueidmap[obj].int
这仍然会产生整数,但由于它们是UUID,因此不能保证它们是唯一的,但是你在生命中遇到相同ID的可能性小于被陨石撞击的可能性。见How unique is UUID?
这样即使对于具有非重叠生命期的对象,它也会为您提供唯一的ID:
>>> class Foo:
... pass
...
>>> id(Foo())
4547149104
>>> id(Foo()) # memory address reused
4547149104
>>> uniqueid(Foo())
151797163173960170410969562162860139237
>>> uniqueid(Foo()) # but you still get a unique UUID
188632072566395632221804340107821543671
id在当前存在的对象中是唯一的。如果垃圾收集器删除了一个对象,则未来的对象可以具有相同的id(并且很可能会)。您必须使用自己的唯一值(例如,某些uuid
)以确保您引用特定对象。您也无法手动进行垃圾回收。
id
值。实际上,如果在销毁第一个对象后立即创建类似对象,则可能会重复使用它。id
不会被重用,因为该对象仍然存在。如果您只是持有id
值,那么您可能做错了什么。