他们似乎仍然有自己的位置 – 比如维护一个响应事件的对象列表.由于不需要对它们进行deref来调用它们弱引用的对象上的方法(与需要首先调用的weakref.ref对象不同),因此在数千次迭代中使用代理对象可以获得一些时间.然而,它们似乎更难以实际识别为“死”,并且一旦它们引用的对象消失就会立即清理.例子 –
>>> mylist #note the object at mylist[1] is already dead... [<weakproxy at 0x10ccfe050 to A at 0x10ccf9f50>,<weakproxy at 0x10ccfe1b0 to NoneType at 0x10cb53538>] >>> for i in mylist[:]: ... try: ... getattr(i,'stuff') #the second arg could be any sentinel; ... #I just want to hit a ReferenceError ... except AttributeError: ... pass ... except ReferenceError: ... mylist.remove(i) ... Traceback (most recent call last): File "<stdin>",line 7,in <module> ReferenceError: weakly-referenced object no longer exists
所以它存在于那里,它实际上不能直接引用或像变量一样传递,因为这显然会导致Python“取消引用”它并使用它弱指向的对象. (或者其他的东西.)
到目前为止,我发现的唯一似乎有点可靠的是通过try / except路由它,但感觉有点像一个小跑.
for i,obj in enumerate(mylist): try: obj.__hash__ except ReferenceError: del mylist[i]
它可以工作,但Python往往是“所有事情的一个正确答案”语言,这感觉就像一个非常大的解决方案 – 我可能错了,但如果列表足够大,不会复制列表以这种方式适应问题?
不幸的是,我似乎真的认为这是一个不涉及类型检查或其他垃圾的潜在解决方案,但我猜我在weakref文档中错过了一些关于如何正确处理weakref.proxy对象的内容.感觉就像’确保weakref.proxy不是特殊情况,因此使用try / except是一次性实用程序.
因此,如果我在使用try / except的假设是正确的,那么有没有更好的方法来识别死的weakref.proxy对象?
编辑:我已经接受了答案,所以谢谢你 – 尝试/除了似乎是唯一可以接受的.
至于为什么我没有使用WeakSets – 允许我发布一个简单的演示,最终导致我使用代理对象.
>>> from weakref import WeakSet,proxy >>> import cProfile >>> class A(object): ... def __init__(self): ... self.x = 0 ... def foo(self,v=1): ... self.x += v ... >>> Stick = A() >>> Dave = A() >>> Jupiter = A() >>> ##just a list of objects,no fancy >>> one_group = [Stick,Dave,Jupiter] >>> cProfile.run("for i in xrange(0,2**16): [x.foo(i) for x in one_group]") 196610 function calls in 0.136 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 196608 0.049 0.000 0.049 0.000 <stdin>:4(foo) 1 0.087 0.087 0.136 0.136 <string>:1(<module>) 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} >>> Stick.x 2147450880 >>> Dave.x 2147450880 >>> Jupiter.x 2147450880
所以我们知道它有效,而且我们知道65k迭代的速度相当快.似乎体面;让我们来看看WeakSets.
>>> ##now a WeakSet of objects. should be ideal; but... >>> two_group = WeakSet((Stick,Jupiter)) >>> cProfile.run("for i in xrange(0,2**16): [x.foo(i) for x in two_group]") 851970 function calls in 0.545 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 196608 0.055 0.000 0.055 0.000 <stdin>:4(foo) 1 0.158 0.158 0.545 0.545 <string>:1(<module>) 65536 0.026 0.000 0.026 0.000 _weakrefset.py:16(__init__) 65536 0.043 0.000 0.051 0.000 _weakrefset.py:20(__enter__) 65536 0.063 0.000 0.095 0.000 _weakrefset.py:26(__exit__) 65536 0.024 0.000 0.024 0.000 _weakrefset.py:52(_commit_removals) 262144 0.159 0.000 0.331 0.000 _weakrefset.py:58(__iter__) 65536 0.009 0.000 0.009 0.000 {method 'add' of 'set' objects} 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 65536 0.008 0.000 0.008 0.000 {method 'remove' of 'set' objects}
过早优化?罗. :)这大约慢4倍.啊呀.
>>> ##now finally,a list of proxy objects >>> three_group = [proxy(x) for x in one_group] >>> cProfile.run("for i in xrange(0,2**16): [x.foo(i) for x in three_group]") 196610 function calls in 0.139 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 196608 0.050 0.000 0.050 0.000 <stdin>:4(foo) 1 0.089 0.089 0.139 0.139 <string>:1(<module>) 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
基于这些数字,我开始认为,维护必须正确注释为已释放的对象列表的最简单,最快的方法是使用代理对象.我认为将try / except作为“清单清除”的策略性使用将确保死代理不会导致错误.
为了方便起见,我可能会回到使用weakref.ref对象的方法,但代理对象的使用对于它们看似“直接”访问对象很有趣.
解决方法
您的第二种方法使用索引而不会触发该异常,在第一个循环中使用它:
remove = set() for index,proxy in enumerate(mylist): try: getattr(proxy,'stuff') except AttributeError: pass except ReferenceError: remove.add(index) mylist = [p for i,p in enumerate(mylist) if i not in remove]
演示:
>>> import weakref >>> class Foo(object): pass ... >>> remove = set() >>> mylist = [weakref.proxy(Foo())] # instant dead proxy >>> for index,proxy in enumerate(mylist): ... try: ... getattr(proxy,'stuff') ... except AttributeError: ... pass ... except ReferenceError: ... remove.add(index) ... >>> remove set([0])
如果你特别想要一个测试对象仍然存在的函数,你可以使用:
def proxy_live(p) try: bool(p) except ReferenceError: return False return True
但是考虑到布尔测试本身可以触发删除对象,如果代理对象挂钩到属性访问,__ nonzero__或__len__方法并触发删除.然后,线程可以导致竞争条件,其中上述函数将返回True,您仍然必须考虑对象上的操作可能引发ReferenceError.
如果订单不重要,我在这里使用weakref.WeakSet()
object;当循环遍历这些时,您将获得实时对象;已经取消引用为你修剪的死亡参考文献,没有竞争条件的风险.