【问题标题】:Python: dereferencing weakproxyPython:取消引用弱代理
【发布时间】:2012-04-20 12:19:08
【问题描述】:

有没有办法从指向它的弱代理中获取原始对象?例如,weakref.proxy() 是否存在相反的情况?

一个简化的例子(python2.7):
import weakref

class C(object):
    def __init__(self, other):
        self.other = weakref.proxy(other)

class Other(object):
    pass

others = [Other() for i in xrange(3)]

my_list = [C(others[i % len(others)]) for i in xrange(10)]

我需要从my_list 获取唯一other 成员的列表。我喜欢此类任务的方式 是使用set:

unique_others = {x.other for x in my_list}

不幸的是,这会引发TypeError: unhashable type: 'weakproxy'

我设法以一种命令式的方式(缓慢而肮脏)解决了特定问题:
unique_others = []
for x in my_list:
    if x.other in unique_others:
        continue
    unique_others.append(x.other)

但标题中指出的一般问题仍然存在。 如果我只有 my_list 受到控制,而 others 被埋在某个库中,并且有人可能随时删除它们,我想通过在列表中收集 nonweak 引用来防止删除怎么办?

或者我可能想获得对象本身的repr(),而不是<weakproxy at xx to Other at xx> 我想应该有类似weakref.unproxy 我不知道的东西。

【问题讨论】:

    标签: python weak-references


    【解决方案1】:

    基本上有类似weakref.unproxy 的东西,但它只是命名为weakref.ref(x)()。 代理对象仅用于委托,并且实现相当不稳定......

    == 函数无法正常工作:

    >>> weakref.proxy(object) == object
    False
    >>> weakref.proxy(object) == weakref.proxy(object)
    True
    >>> weakref.proxy(object).__eq__(object)
    True
    

    但是,我看到您不想一直调用 weakref.ref 对象。具有取消引用支持的良好工作代理会很好。

    但目前,这是不可能的。如果你查看你看到的python内置源代码,你需要像PyWeakref_GetObject这样的东西,但是根本没有调用这个方法(并且:如果参数错误,它会引发PyErr_BadInternalCall,所以它似乎是一个内部函数)。 PyWeakref_GET_OBJECT 使用得更多,但weakref.py 中没有任何方法可以做到这一点。

    所以,很抱歉让您失望了,但您 weakref.proxy 并不是大多数人想要的用例。但是,您可以制作自己的 proxy 实现。这并不难。只需在内部使用weakref.ref 并覆盖__getattr____repr__ 等。

    关于 PyCharm 如何产生正常的repr 输出的小旁注(因为您在评论中提到了这一点):

    >>> class A(): pass
    >>> a = A()
    >>> weakref.proxy(a)
    <weakproxy at 0x7fcf7885d470 to A at 0x1410990>
    >>> weakref.proxy(a).__repr__()
    '<__main__.A object at 0x1410990>'
    >>> type( weakref.proxy(a))
    <type 'weakproxy'>
    

    如您所见,调用原来的__repr__ 真的很有帮助!

    【讨论】:

      【解决方案2】:

      我知道这是一个老问题,但我最近一直在寻找答案并想出了一些办法。就像其他人说的那样,没有记录的方法可以做到这一点,并且查看弱代理类型的实现可以确认没有标准的方法可以实现这一点。

      我的解决方案使用了这样一个事实,即所有 Python 对象都有一组标准方法(如 __repr__)并且绑定的方法对象包含对实例的引用(在 __self__属性)。

      因此,通过解引用代理来获取方法对象,我们可以从方法对象中获得对被代理对象的强引用。

      例子:

      >>> def func():
      ...    pass
      ...
      >>> weakfunc = weakref.proxy(func)
      >>> f = weakfunc.__repr__.__self__
      >>> f is func
      True
      

      另一个好处是它也适用于强引用:

      >>> func.__repr__.__self__ is func
      True
      

      因此,如果可以预期代理或强引用,则无需进行类型检查。

      编辑:

      我刚刚注意到这不适用于类代理。那么这不是通用的。

      【讨论】:

        【解决方案3】:

        weakref.ref 是可散列的,而weakref.proxy 不是。 API 没有说明如何实际获取代理指向的对象的句柄。使用weakref,这很容易,你可以调用它。因此,您可以推出自己的类似代理的类...这是一个非常基本的尝试:

        import weakref
        class C(object):
           def __init__(self,obj):
              self.object=weakref.ref(obj)
           def __getattr__(self,key):
              if(key == "object"): return object.__getattr__(self,"object")
              elif(key == "__init__"): return object.__getattr__(self,"__init__")
              else:
                  obj=object.__getattr__(self,"object")() #Dereference the weakref
                  return getattr(obj,key)
        
        class Other(object):
           pass
        
        others = [Other() for i in range(3)]
        
        my_list = [C(others[i % len(others)]) for i in range(10)]
        
        unique_list = {x.object for x in my_list}
        

        当然,现在 unique_list 包含 refs,而不是根本不同的 proxys...

        【讨论】:

        • API 没有,但像 PyCharm 这样的 IDE 能够以某种方式在监视列表窗口中显示 repr() 的结果。
        • @robyschek 当然可能的。我相信你可以在标准库的某个地方找到代码并弄清楚代理对象是如何工作的......但是如果它没有在 API 中记录,你不能保证它可以移植到其他 python 实现中...... .
        【解决方案4】:

        我知道这是一个老问题,但我一直被它所困扰(因此,标准库中没有真正的“非代理”)并想分享我的解决方案...

        我解决它以获取真实实例的方法只是创建一个返回它的属性(尽管我建议使用weakref.ref 而不是weakref.proxy,因为代码应该在访问它之前检查它是否仍然存在,而不是让记住在访问任何属性时捕获异常)。

        无论如何,如果您仍然必须使用代理,获取真实实例的代码是:

        import weakref
        
        class MyClass(object):
        
            @property
            def real_self(self):
                return self
        
        instance = MyClass()
        proxied = weakref.proxy(instance)
        assert proxied.real_self is instance
        

        【讨论】:

          猜你喜欢
          • 2021-02-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多