34

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

dereference(id(a)) == a

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

4

4 回答 4

46

这是一个基于“Tiran”在博客讨论@Hophat Abc 在他自己的答案中引用的(现已删除的)评论的实用函数,该函数在 Python 2 和 3 中都可以使用。

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

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
于 2013-02-21T21:44:35.133 回答
9

不容易。

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

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

于 2013-02-21T20:38:10.793 回答
6

有几种方法,不难做到:

在 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]

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

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

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

于 2013-02-21T20:51:26.960 回答
3

注意:更新到 Python 3。

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

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

#!/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

于 2013-02-21T22:31:41.817 回答