当前位置: 首页 > 面试题库 >

Python的id()有多独特?

温嘉赐
2023-03-14
问题内容

tl; dr Python是否会重用ID?生命周期不重叠的两个对象获得相同ID的可能性有多大?

背景: 我一直在从事一个纯粹用Python
3编写的复杂项目。我一直在测试中看到一些问题,并花了大量时间寻找根本原因。经过一些分析,我怀疑当测试整体运行时(由专门的调度员精心策划并运行),它正在重用某些模拟方法,而不是用其原始方法实例化新对象。为了检查html" target="_blank">解释器是否正在重用,我使用id()

问题:
id()通常可以正常工作并显示对象标识符,并让我知道我的呼叫何时创建新实例而不重用。但是,如果两个对象相同,则id会怎样?该文件说:

返回对象的“身份”。这是一个整数,可以保证在此对象的生存期内唯一且恒定。具有非重叠生存期的两个对象可能具有相同的id()值。

问题:

  1. 解释器何时可以重用id()值?只是当它随机选择相同的存储区时?如果只是随机的,这似乎极不可能,但仍不能保证。

  2. 还有其他方法可以检查我实际引用的对象吗?我遇到了一个对象,它有一个模拟方法的情况。该对象不再使用,垃圾收集器将其销毁。之后,我创建了一个相同类的新对象,它得到了一个新对象,id()但是该方法具有与 模拟时相同的ID,实际上 只是一个模拟。

  3. 有没有一种方法可以强制Python破坏给定的对象实例?从我的阅读看来,似乎没有,这取决于垃圾回收器,因为它看不到对该对象的引用,但是我认为还是值得一问。


问题答案:

是的,CPython重用了id()值。 不要指望这些在Python程序中是唯一的

这是明确记载:

返回对象的“身份”。这是一个整数,可以保证在 此对象的生存期内 唯一且恒定 。 具有不重叠生存期的两个对象可能具有相同的id()值。

大胆强调我的。仅当对象 存在时 ,id才是唯一的。没有剩余引用的对象将从内存中删除,从而允许将该id()值重新用于另一个对象,因此使用了
非重叠生命周期的 措辞。

请注意,这仅适用于CPython,这是python.org提供的标准实现。还有其他Python实现,例如IronPython,Jython和PyPy,它们对实现方式id()都有自己的选择,因为它们每个都可以对如何处理内存和对象生存期做出不同的选择。

要解决您的特定问题:

  1. 在CPython中,id()是内存地址。新对象将被放入下一个可用的存储空间中,因此,如果特定的内存地址具有足够的空间来容纳下一个新对象,则该存储地址将被重用。创建相同大小的新对象时,您可以在解释器中看到以下内容:
    >>> id(1234)
    

    4546982768

    id(4321)
    4546982768

1234字面创建一个新的整数对象,为此id()产生一个数值。由于没有对该int值的进一步引用,因此将其再次从内存中删除。但是使用不同的整数文字再次执行相同的表达式,很可能会看到相同的id()值(运行垃圾回收破坏循环引用可能会释放更多的内存,因此您
不会id()再次看到相同的值。

因此它 不是随机的 ,但在CPython中是内存分配算法的函数。

  1. 如果需要检查特定对象, 请保留对它的引用 。如果您只需要确保对象仍然处于“活动状态”,那么这可能是一个weakref 较弱的参考

例如,先记录一个对象引用,然后再检查它:

    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其他)。

您可以使用这种机制来生成真正唯一的标识符,请参见下文。

  1. 要“破坏”对象,您要做的就是 删除 对该对象的 所有引用 。变量(本地和全局)是引用。其他对象的属性以及列表,元组,字典,集合等容器中的条目也是如此。

一旦所有对一个对象的引用都消失了,该对象上的引用计数就会降为0,然后在该位置被删除。

仅需要垃圾回收即可破坏 循环引用 ,即仅互相 引用的
对象,而无需进一步引用循环。因为这样的一个循环在没有帮助的情况下永远不会达到0的引用计数,因此垃圾收集器会定期检查这种循环并中断其中一个引用以帮助从内存中清除那些对象。

因此,通过删除对对象的所有引用,可以使它从内存中删除(释放)。如何实现取决于对象的引用方式。你可以要求译员告诉你哪些对象引用与给定对象gc.get_referrers()的功能,但考虑到
不给你变量名
。它为您提供对象,例如字典对象,该对象是将__dict__对象引用为全局对象的模块的属性,等等。对于完全在您控制之下的代码,最多gc.get_referrers()用作提醒自己从何处引用对象的工具当您编写代码删除这些代码时。

如果必须在 Python应用程序 的生存期内具有唯一的标识符,则必须实现自己的工具。如果您的对象是可 哈希的
并且支持弱引用,那么您可以使用WeakKeyDictionary实例将任意对象与UUID关联:

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 你的
一生是不是被陨石击中小。

这样,即使对于具有非重叠生命周期的对象,这也会为您提供唯一的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


 类似资料:
  • 问题内容: php会话ID有多独特?我从阅读过的各种东西中得到的印象是,我不应该依赖永远不会获得相同sessionid的两个用户。它不是GUID吗? 问题答案: Session_id确实可以重复,但是概率很低。如果您的网站访问量不错,那么它可能会在您的网站生命中发生一次,并且只会使一个会话的用户烦恼。 除非您期望建立一个流量很高的网站或为银行业提供服务的服务,否则这并不值得关注。

  • 问题内容: 这个问题实际上并不是寻找解决方案的问题,而仅仅是出于好奇。PHP uniqid函数具有一个更熵的标志,以使输出“更唯一”。这让我想知道,当more_entropy为true时(而不是true时),此函数多次产生相同结果的可能性有多大。换句话说,启用more_entropy时与禁用时相比,uniqid的唯一性如何?始终启用more_entropy是否有任何弊端? 问题答案: 2014年3

  • 问题内容: 我需要从单台PC生成唯一的代码,我的软件将通过许可证进行分发,并且我需要在本地识别环境并将此代码发送到服务器。我不想在服务器上保留一个序列号,并且每个客户端都收到一个序列号,我试图从NetworkInterfaces读取MAC地址,但并不安全,用户可以轻松更改MAC地址。我认为最好的方法是读取主板序列号,但直到现在我还没有找到方法。有什么建议吗? 问题答案: 您的软件值多少钱? 目标市

  • 问题内容: 是否可以采用以下方式构成的表: 最终变成这样的表: 我已经研究过使用枢轴,但无法使其正常工作。 我目前每个月都使用CROSS APPLY表值函数。 有一个更好的方法吗? 编辑:添加了现有查询-试图简化显示: 功能看起来像 问题答案: 您不需要多个子查询。答案很容易-使用集合理论。从您的第一个表ID / Month / Info1 / Info2轻松合并即可完成ID / Month +(

  • 问题内容: 我可以给它加上浮点数,例如 但是它有多精确?如果我给它 它真的会睡约50毫秒吗? 问题答案: time.sleep函数的准确性取决于你底层操作系统的睡眠准确性。对于非实时操作系统(如普通Windows),你可以睡眠的最小间隔约为10-13毫秒。在最小的10-13毫秒以上的时间里,我已经看到了几毫秒内的准确睡眠。 更新:就像在下面引用的文档中提到的那样,通常以循环方式进行睡眠,以确保在早

  • 我试图设置一个独立的Spark2.0服务器来并行处理分析功能。为此,我希望有一个带有多个执行者的工作人员。 我在使用: 独立Spark 2.0 8核 24gig ram Windows server 2008 pyspark(尽管这看起来不相关)