【问题标题】:Is it possible to dereference variable id's?是否可以取消引用变量 id?
【发布时间】:2013-02-07 07:59:05
【问题描述】:

您能否取消引用从 Python 中的 id 函数检索到的变量 id?例如:

dereference(id(a)) == a

我想从学术角度了解;我知道还有更实用的方法。

【问题讨论】:

    标签: python memory dereference


    【解决方案1】:

    注意:已更新至 Python 3。

    这里的另一个答案改编自另一个comment,这个来自“Peter Fein”,在@Hophat Abc 在他自己对自己问题的回答中引用的讨论中。

    虽然不是一个一般性的答案,但在您知道要查找其 id 的对象的类的情况下可能仍然有用——而不是它们是 anything 的 id。我觉得这可能是一种值得的技术,即使有这个限制(并且没有我的other answer 的安全问题)。基本思想是创建一个跟踪自身实例和子类的类。

    #!/usr/bin/env python3
    import weakref
    
    class MetaInstanceTracker(type):
        """ Metaclass for InstanceTracker. """
    
        def __new__(cls, name, bases, dic):
            cls = super().__new__(cls, name, bases, dic)
            cls.__instances__ = weakref.WeakValueDictionary()
            return cls
    
    class InstanceTracker(object, metaclass=MetaInstanceTracker):
        """ Base class that tracks instances of its subclasses using weakreferences. """
    
        def __init__(self, *args, **kwargs):
            self.__instances__[id(self)]=self
            super().__init__(*args, **kwargs)
    
        @classmethod
        def find_instance(cls, obj_id):
            return cls.__instances__.get(obj_id, None)
    
    
    if __name__ == '__main__':
    
        class MyClass(InstanceTracker):
            def __init__(self, name):
                super(MyClass, self).__init__()
                self.name = name
            def __repr__(self):
                return '{}({!r})'.format(self.__class__.__name__, self.name)
    
        obj1 = MyClass('Bob')
        obj2 = MyClass('Sue')
    
        print(MyClass.find_instance(id(obj1)))  # -> MyClass('Bob')
        print(MyClass.find_instance(id(obj2)))  # -> MyClass('Sue')
        print(MyClass.find_instance(42))        # -> None
    
    

    【讨论】:

      【解决方案2】:

      这是一个实用函数,它基于“Tiran”在博客 discussion @Hophat Abc 在 his own answer 中的引用(现已删除)发表的评论,该函数适用于 Python 2 和 3。

      免责声明:如果您阅读链接的讨论,您会发现有些人认为这是不安全的,不应该永远使用它(同样在下面的一些cmets)。我不同意这种评估,但我觉得我至少应该提一下关于使用它的一些争论。

      import _ctypes
      
      def di(obj_id):
          """ Inverse of id() function. """
          return _ctypes.PyObj_FromPtr(obj_id)
      
      if __name__ == '__main__':
          a = 42
          b = 'answer'
          print(di(id(a)))  # -> 42
          print(di(id(b)))  # -> answer
      

      【讨论】:

      • 但请注意,如果您向解释器传递无效地址(例如,因为它已被垃圾收集),这将导致解释器出现段错误。
      • 段错误,或更糟。例如,现在可能有其他对象存在,您的程序将默默地与错误的对象一起突突,并因此做出错误的决定,删除您的文件或泄露您的用户数据或加速您的程序应该使用的带锯控制或更糟。
      • FWIW,我实际上已经使用了类似的东西来解决JSON module 的一些限制。我认为有些场景“足够安全”可以考虑使用。
      • @martineau:我认为这不安全,而且肯定不够安全,无法在 Stack Overflow 上发帖供人们复制,甚至没有意识到它在做不安全的事情。遍历输入以查找所有 NoIndent 实例会好得多,而不是尝试通过直接内存访问从 ID 转到对象。 PyObj_FromPtr 的使用是一个崩溃风险和安全问题,如果有可能在输出中出现一个看起来像 @@123@@ 的字符串而不来自 NoIndent,即使这不会发生在您身上,它也可能发生给任何使用你答案的人。
      • 或者,由于您可以在 default 方法中访问 NoIndent 对象,因此您可以在那里保存对它的引用(然后清除 encode 中保存的引用或其他内容避免让对象保持活动状态)。
      【解决方案3】:

      有几种方法,不难做到:

      在 O(n) 中

      In [1]: def deref(id_):
         ....:     f = {id(x):x for x in gc.get_objects()}
         ....:     return f[id_]
      
      In [2]: foo = [1,2,3]
      
      In [3]: bar = id(foo)
      
      In [4]: deref(bar)
      Out[4]: [1, 2, 3]
      

      平均而言,来自 cmets 的更快方式(感谢@Martijn Pieters):

      def deref_fast(id_):
          return next(ob for ob in gc.get_objects() if id(ob) == id_)
      

      最快的解决方案是@martineau 的回答,但确实需要暴露 python 内部。上述解决方案使用标准 python 构造。

      【讨论】:

      • 您现在创建了对内存中的每个值的引用,以确保它们不会被垃圾收集。你真的想改用weakref module
      • OP 是从学术角度而非实际角度提出的。
      • 但是 OP 不会是唯一关注这个的人;创建 id-to-object 映射的想法很好,但它有一些你需要注意的后果。
      • @MartijnPieters:是的,但只有在函数返回并且局部变量 f 不再存在时所有的东西都消失了,对吧?
      • @MartijnPieters:是的,重用会有问题——有一刻我以为你是说如果WeakValueDictionary() 寿命更长,这个想法可能是可以挽救的。无论如何,这个答案与普通的dict 一样,起作用,尽管有点慢。
      【解决方案4】:

      不容易。

      您可以通过gc.get_objects() 列表进行递归,测试每个对象是否具有相同的id(),但这不是很实用。

      id() 函数不打算被取消引用;它基于内存地址这一事实是 CPython 的实现细节,其他 Python 实现不遵循。

      【讨论】:

      • 给定:a=1b=ac=a...a、b、c 都具有相同的id()
      • 当然,他们有相同的 id。这与 OP 问题有什么关系?
      • 请注意,变量的引用都是同一个对象。
      • @Jonathan Vanasco:OP 询问如何在 object 给定它的 id() 的情况下找到它,而不是它的名字——除了有多个之外,它还可以也没有。