【问题标题】:Get object by id()? [duplicate]通过 id() 获取对象? [复制]
【发布时间】:2010-11-26 16:03:35
【问题描述】:

假设我有一个 Python 对象的 id,我通过 id(thing) 检索到该对象。如何通过给我的 id 号再次找到thing

【问题讨论】:

  • 我很好奇:你为什么要这样做?你的目标是什么?
  • 我无法重新找到它的来源,但我认为 id() 返回的是特定发行版想要的任何内容。此时,重述的 CPython 可能会返回类似内存的地址,但其他发行版可能会返回不同的对象类型或不是内存指针的整数。如果有一个内置函数可以通过 id() 返回的内容获取对象,那就太好了。尽管很难想象其他持久性用例。此外,虽然 *variable, like C 似乎是有道理的;我喜欢 Python,因为它不像大多数其他语言那样使用标点符号。
  • 这样做的正当理由:调试。对象的默认repr 包括它们的内存地址。有时在调试时(尤其是交互式),您希望能够访问该对象,而无需尝试深入了解它的定义位置。
  • @asmeurer:我认为这样做还有其他正当理由(假设使用 CPython)。我在这里对其他问题的回答中至少使用过几次。

标签: python


【解决方案1】:

这样就可以了:

a = 0
id_a = id(a)
variables = {**locals(), **globals()}
for var in variables:
    exec('var_id=id(%s)'%var)
    if var_id == id_a:
        exec('the_variable=%s'%var)
print(the_variable)
print(id(the_variable))

但我建议实施更体面的方式。

【讨论】:

    【解决方案2】:

    如果对象还在,可以ctypes

    import ctypes
    a = "hello world"
    print ctypes.cast(id(a), ctypes.py_object).value
    

    输出:

    hello world
    

    如果您不知道对象是否仍然存在,这会导致未定义的行为和奇怪的崩溃或更糟,所以要小心。

    【讨论】:

    • 无论如何,在今天的 CPython 中。 :^)
    • 这是一个完美的答案!
    • @HamidFzM 不,不是真的。如果我有一个 ID,我什至不知道该对象是否仍然存在。
    • repr 输出 id(a) 的十六进制表示,为了使用 ctypes,必须使用 int(hexid, 0) 将其转换回十进制。只是我的两分钱
    • 有没有办法在这样做之前先检查内存地址是否存在?如果您传递了无效值(例如,因为它已被垃圾回收),解释器会出现段错误。
    【解决方案3】:

    为了完整起见,仅提及此模块。 This code by Bill Bumgarner 包含一个 C 扩展来执行您想要的操作,而无需循环遍历存在的每个对象。

    函数的代码非常简单。每个 Python 对象在 C 中都由指向 a PyObject struct 的指针表示。因为id(x) 只是这个结构体的内存地址,我们可以通过将x 当作指向PyObject 的指针来检索Python 对象,然后调用Py_INCREF 告诉垃圾收集器我们正在创建一个对对象的新引用。

    static PyObject *
    di_di(PyObject *self, PyObject *args)
    {
        PyObject *obj;
        if (!PyArg_ParseTuple(args, "l:di", &obj))
            return  NULL;
    
        Py_INCREF(obj);
        return obj;
    }
    

    如果原始对象不再存在,则结果未定义。它可能会崩溃,但它也可能返回对新对象的引用,该新对象在内存中获取旧对象的位置。

    【讨论】:

      【解决方案4】:

      您可能需要考虑以另一种方式实现它。你知道weakref模块吗?

      (已编辑)Python weakref module 允许您保留对对象的引用、字典引用和代理,而无需将这些引用计入引用计数器。它们就像符号链接。

      【讨论】:

      • 有时不能创建对对象的弱引用,例如:TypeError: cannot create weak reference to 'lxml.etree._Element' object
      【解决方案5】:

      eGenix mxTools 库确实提供了这样的功能,尽管标记为“仅限专家”:mx.Tools.makeref(id)

      【讨论】:

        【解决方案6】:

        简短的回答,你不能。

        长答案,您可以维护一个用于将 ID 映射到对象的字典,或者通过详尽搜索 gc.get_objects() 来查找 ID,但这会产生两个问题之一:字典的引用将保持对象的活动状态并阻止GC,或者(如果它是 WeakValue dict 或者您使用 gc.get_objects())ID 可能会被释放并重新用于完全不同的对象。

        基本上,如果您尝试这样做,您可能需要做一些不同的事情。

        【讨论】:

        • 我一直打算把对象__del__中的id引用扔掉,你认为这样可以确保事情不会中断吗?
        • +1:同意:不要这样做。只需使用适当的键创建一个适当的对象字典 - 你会更快乐。
        • @cool-RR:是的,你应该放心。
        • 我喜欢你建议的解决方案。这一点都不坏。它实际上非常出色。
        【解决方案7】:

        您可以使用gc 模块来获取Python 垃圾收集器当前跟踪的所有对象。

        import gc
        
        def objects_by_id(id_):
            for obj in gc.get_objects():
                if id(obj) == id_:
                    return obj
            raise Exception("No found")
        

        【讨论】:

        • 这有一个别名问题:在过去任意点获得的 ID 现在可能引用不同的对象。
        • 只要您维护了对该对象的引用,就不会发生这种情况。同样,这通常是个坏主意。
        • 我同意其他许多评论者的观点:不要这样做。制作你自己的对象字典。
        猜你喜欢
        • 2011-09-03
        • 1970-01-01
        • 2011-01-25
        • 1970-01-01
        • 1970-01-01
        • 2012-06-30
        • 1970-01-01
        相关资源
        最近更新 更多