Python: dereferencing weakproxy

随声附和 提交于 2021-02-06 15:48:08

问题


Is there any way to get the original object from a weakproxy pointed to it? eg is there the inverse to weakref.proxy()?

A simplified example(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)]

I need to get the list of unique other members from my_list. The way I prefer for such tasks is to use set:

unique_others = {x.other for x in my_list}

Unfortunately this throws TypeError: unhashable type: 'weakproxy'

I have managed to solve the specific problem in an imperative way(slow and dirty):
unique_others = []
for x in my_list:
    if x.other in unique_others:
        continue
    unique_others.append(x.other)

but the general problem noted in the caption is still active. What if I have only my_list under control and others are burried in some lib and someone may delete them at any time, and I want to prevent the deletion by collecting nonweak refs in a list?

Or I may want to get the repr() of the object itself, not <weakproxy at xx to Other at xx> I guess there should be something like weakref.unproxy I'm not aware about.

回答1:


Basically there is something like weakref.unproxy, but it's just named weakref.ref(x)(). The proxy object is only there for delegation and the implementation is rather shaky...

The == function doesn't work as you would expect it:

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

However, I see that you don't want to call weakref.ref objects all the time. A good working proxy with dereference support would be nice.

But at the moment, this is just not possible. If you look into python builtin source code you see, that you need something like PyWeakref_GetObject, but there is just no call to this method at all (And: it raises a PyErr_BadInternalCall if the argument is wrong, so it seems to be an internal function). PyWeakref_GET_OBJECT is used much more, but there is no method in weakref.py that could be able to do that.

So, sorry to disappoint you, but you weakref.proxy is just not what most people would want for their use cases. You can however make your own proxy implementation. It isn't to hard. Just use weakref.ref internally and override __getattr__, __repr__, etc.

On a little sidenote on how PyCharm is able to produce the normal repr output (Because you mentioned that in a comment):

>>> 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'>

As you can see, calling the original __repr__ can really help!




回答2:


I know this is an old question but I was looking for an answer recently and came up with something. Like others said, there is no documented way to do it and looking at the implementation of weakproxy type confirms that there is no standard way to achieve this.

My solution uses the fact that all Python objects have a set of standard methods (like __repr__) and that bound method objects contain a reference to the instance (in __self__ attribute).

Therefore, by dereferencing the proxy to get the method object, we can get a strong reference to the proxied object from the method object.

Example:

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

Another nice thing is that it will work for strong references as well:

>>> func.__repr__.__self__ is func
True

So there's no need for type checks if either a proxy or a strong reference could be expected.

Edit:

I just noticed that this doesn't work for proxies of classes. This is not universal then.




回答3:


weakref.ref is hashable whereas weakref.proxy is not. The API doesn't say anything about how you actually can get a handle on the object a proxy points to. with weakref, it's easy, you can just call it. As such, you can roll your own proxy-like class...Here's a very basic attemp:

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}

Of course, now unique_list contains refs, not proxys which is fundamentally different...




回答4:


I know that this is an old question, but I've been bitten by it (so, there's no real 'unproxy' in the standard library) and wanted to share my solution...

The way I solved it to get the real instance was just creating a property which returned it (although I suggest using weakref.ref instead of a weakref.proxy as code should really check if it's still alive before accessing it instead of having to remember to catch an exception whenever any attribute is accessed).

Anyways, if you still must use a proxy, the code to get the real instance is:

import weakref

class MyClass(object):

    @property
    def real_self(self):
        return self

instance = MyClass()
proxied = weakref.proxy(instance)
assert proxied.real_self is instance


来源:https://stackoverflow.com/questions/10246116/python-dereferencing-weakproxy

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!