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

5 python weak-references

因此weakref.proxy,weakref.ref当涉及检查引用是否为实时,或者"解引用"它们,或实际上几乎任何方式实际上,对象根本不像对象一样工作.:P

他们似乎仍然有自己的位置 - 比如维护一个响应事件的对象列表.由于一个不需要DEREF它来调用它们是弱引用(不像对象的方法weakref.ref需要被称为第一对象),一个人获得一定的时间从使用的背proxy过几千次迭代的对象.然而,它们似乎更难以实际识别为"死",并且一旦它们引用的对象消失就会立即清理.例子 -

>>> 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
Run Code Online (Sandbox Code Playgroud)

所以它存在于那里,它实际上不能直接引用或像变量一样传递,因为这显然会导致Python"取消引用"它并使用它弱指向的对象.(或者其他的东西.)

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

for i, obj in enumerate(mylist):
    try:
        obj.__hash__
    except ReferenceError:
        del mylist[i]
Run Code Online (Sandbox Code Playgroud)

它可以工作,但Python往往是"所有事情的一个正确答案"语言,这感觉就像一个非常大的解决方案 - 我可能错了,但如果列表足够大,不会复制列表以这种方式适应问题?

不幸的是,这是所有我能似乎真的觉得作为一个潜在的解决,不涉及类型检查或其他垃圾,但我猜我只是错过了的东西weakref如何处理文档weakref.proxy适当的对象.它'感觉''希望确保weakref.proxy不是特殊情况,所以使用a 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
Run Code Online (Sandbox Code Playgroud)

所以我们知道它有效,而且我们知道65k迭代的速度相当快.似乎体面; 让我们来看看WeakSets.

>>> ##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}
Run Code Online (Sandbox Code Playgroud)

过早优化?罗.:)这大约慢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}
Run Code Online (Sandbox Code Playgroud)

基于这些数字,我开始认为,维护必须正确注释为已释放的对象列表的最简单,最快的方法是使用代理对象.我认为战略性地使用try/except"列表清理器"将确保死代理不会导致错误.为了方便起见,我可能还会回到使用weakref.ref对象的方法,但代理对象的使用对于它们看似"直接"访问对象很有趣.

Mar*_*ers 3

您的弱引用对象会再次引发ReferenceError异常,而不是在循环或访问时,很可能是因为 Python 尝试使用代理上的方法。mylist.remove(i)__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]
Run Code Online (Sandbox Code Playgroud)

演示:

>>> 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])
Run Code Online (Sandbox Code Playgroud)

如果您特别想要一个测试对象是否仍然存在的函数,您可以使用:

def proxy_live(p)
    try:
        bool(p)
    except ReferenceError:
        return False
    return True
Run Code Online (Sandbox Code Playgroud)

但要考虑到布尔测试本身可能会触发对象的删除,如果代理对象挂钩到属性访问、或__nonzero__方法__len__并触发删除。然后,该函数和线程可能会导致竞争条件,其中上述函数将返回True,并且您仍然需要考虑对象上的操作可能会引发ReferenceError.

如果顺序不重要,我会在这里使用一个weakref.WeakSet()对象;当循环这些时,你会得到活动的对象;已经取消引用并为您修剪了死引用,没有竞争条件的风险。