有没有办法从指向它的弱代理中获取原始对象? 例如,weakref.proxy()
有相反吗?
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
被埋在某个库中,并且有人可以随时删除它们,并且我想通过在列表中收集非弱引用来防止删除怎么办?
repr()
,而不是<weakproxy at xx to Other at xx>
我想应该有类似我不知道weakref.unproxy
的东西。
这是一个老问题,但我最近正在寻找答案并想出了一些东西。就像其他人说的那样,没有记录的方法可以做到这一点,并且查看弱代理类型的实现确认没有标准方法来实现这一目标。
我的解决方案使用的事实是,所有 Python 对象都有一组标准方法(如 __repr__),并且绑定方法对象包含对实例的引用(在 __self__ 属性中)。
因此,通过取消引用代理以获取方法对象,我们可以从方法对象中获取对代理对象的强引用。
例:
>>> def func():
... pass
...
>>> weakfunc = weakref.proxy(func)
>>> f = weakfunc.__repr__.__self__
>>> f is func
True
另一个好处是它也适用于强引用:
>>> func.__repr__.__self__ is func
True
因此,如果可以预期代理或强引用,则无需进行类型检查。
编辑:
我刚刚注意到这不适用于类的代理。这不是普遍的。
基本上有类似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__
确实会有所帮助!
weakref.ref 是可哈希的,而 weakref.proxy 不是。 API 没有说明您如何实际获取代理指向的对象句柄。 使用弱参考,这很容易,你可以调用它。 因此,您可以滚动自己的类似代理的类...这是一个非常基本的尝试:
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包含ref
s,而不是proxy
s,这是根本不同的......
这是一个老问题,但我被它咬了(所以,标准库中没有真正的"非代理"),想分享我的解决方案......
我解决它以获取真实实例的方法只是创建一个返回它的属性(尽管我建议使用 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