Python - 如何调节weakproxy对象的列表

时间:2014-05-14 21:11:43

标签: python weak-references

因此,weakref.proxy对象在检查引用是否有效或“解除引用”方面似乎完全不像weakref.ref个对象那样工作。实际上,他们,或者实际上几乎任何方式。 :P

他们似乎仍然有自己的位置 - 比如维护一个响应事件的对象列表。由于不需要对它们进行deref来调用它们弱引用的对象上的方法(与需要首先调用的weakref.ref对象不同),因此使用{{1数千次迭代的对象。然而,他们似乎更难以确定为“死亡”。并且一旦他们引用的对象消失就立即清理。例子 -

proxy

所以它存在于那里,它不能直接引用或像变量那样传递,因为这显然会导致Python解除引用'它与它虚弱指向的对象一起工作。 (或者其他什么。)

到目前为止,我发现的唯一似乎有点可靠的是通过try / except路由它,但感觉有点像一个小跑。

>>> 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倾向于成为所有事物的正确答案。语言,这感觉就像一个非常重要的解决方案 - 我可能错了,但如果列表足够大,不以这种方式复制列表会导致问题吗?

不幸的是,我似乎真的认为这是一个潜在的解决方案,不会涉及类型检查或其他垃圾,但我猜测我只是错过了for i, obj in enumerate(mylist): try: obj.__hash__ except ReferenceError: del mylist[i] 中的某些内容有关如何正确处理weakref个对象的文档。它感觉像是&#39;希望确保weakref.proxy不是特殊的情况,因此使用weakref.proxy是一次性实用程序。

因此,如果我在这里假设使用try/except是正确的,那么有更好的方法来识别死try/except个对象吗?

编辑:我已经接受了答案,所以谢谢你 - 尝试/除了似乎是唯一可以接受的答案。

至于为什么我没有使用WeakSets - 请允许我发布一个简单的演示,最终让我使用代理对象。

weakref.proxy

所以我们知道它有效,而且我们知道65k迭代的速度相当快。似乎体面;让我们来看看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

过早优化?罗。 :)这大约慢了4倍。啊呀。

>>> ##now a WeakSet of objects. should be ideal; but...
>>> two_group = WeakSet((Stick, Dave, 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}

基于这些数字,我认为用于维护必须正确注释为已释放的对象列表的最简单和最快的方法是使用代理对象。我认为战略性地使用>>> ##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} 作为清单&#39;将确保死代理不会导致错误。 为了方便起见,我可能会回到使用try/except个对象的方法,但代理对象的使用对他们看似直接的&#39;而言很有趣。访问该对象。

1 个答案:

答案 0 :(得分:3)

您的弱引用对象会在ReferenceError行上再次引发mylist.remove(i)异常 ,而不是在循环或访问时,很可能是因为Python尝试使用{{1}代理上的方法。

您的第二种方法使用索引而不会触发该异常,在第一个循环中使用它:

__eq__

演示:

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__并且你仍然必须考虑到对象上的动作可以引发{{1 }}

如果订单不重要,我会在这里使用weakref.WeakSet() object;当循环遍历这些时,您将获得实时对象;已经取消引用为你修剪的死亡参考文献,没有竞争条件的风险。