Ed *_*hne 4 python weak-references
问题安全遍历WeakKeyDictionary和WeakValueDictionary没把我放心,因为我所希望的,而它的老不够,它的价值再次询问,而不是评论。
假设我有一个MyHashable可哈希的类,并且我想构建一个WeakSet:
obj1 = MyHashable()
obj2 = MyHashable()
obj3 = MyHashable()
obj2.cycle_sibling = obj3
obj3.cycle_sibling = obj2
ws = WeakSet([obj1, obj2, obj3])
Run Code Online (Sandbox Code Playgroud)
然后,我删除一些局部变量,并转换为列表,为以后的循环做准备:
del obj2
del obj3
list_remaining = list(ws)
Run Code Online (Sandbox Code Playgroud)
我举这个问题似乎声称这是蛮好的,但即使没有任何明确的for循环,我已不是已经冒着循环垃圾收集器的构造过程中踢list_remaining和更改集的大小?我希望这个问题非常罕见,以至于很难通过实验检测出来,但是一旦我的程序崩溃,它就会变成蓝色的月亮。
我什至不觉得那个帖子上的各种评论者是否真的同意了
for obj in list(ws):
...
Run Code Online (Sandbox Code Playgroud)
可以,但是他们似乎都以为list(ws)自己可以一直运行而不会崩溃,我什至不敢相信。list构造函数是否避免以某种方式使用迭代器,从而不关心集合大小的变化?是否可以在list构造函数期间不进行垃圾回收,因为它list是内置的?
就目前而言,我写我的代码,以破坏性pop的物品出来的WeakSet,从而完全避免迭代器。我不介意破坏性地这样做,因为在我的代码中,WeakSet无论如何我已经完成了。但是我不知道自己是否偏执。
文档令人沮丧地缺乏有关此方面的信息,但是从实现的角度来看,我们可以WeakSet.__iter__防范这种问题。
在上的迭代期间WeakSet,weakref回调会将引用添加到待处理的删除列表中,而不是直接从基础集中删除引用。如果元素在迭代到达之前就死了,则迭代器不会产生该元素,但是您不会得到segfault或a RuntimeError: Set changed size during iteration或其他任何东西。
这里是警卫(尽管有评论说,不是线程安全的):
class _IterationGuard:
# This context manager registers itself in the current iterators of the
# weak container, such as to delay all removals until the context manager
# exits.
# This technique should be relatively thread-safe (since sets are).
def __init__(self, weakcontainer):
# Don't create cycles
self.weakcontainer = ref(weakcontainer)
def __enter__(self):
w = self.weakcontainer()
if w is not None:
w._iterating.add(self)
return self
def __exit__(self, e, t, b):
w = self.weakcontainer()
if w is not None:
s = w._iterating
s.remove(self)
if not s:
w._commit_removals()
Run Code Online (Sandbox Code Playgroud)
这是__iter__使用警卫的地方:
class WeakSet:
...
def __iter__(self):
with _IterationGuard(self):
for itemref in self.data:
item = itemref()
if item is not None:
# Caveat: the iterator will keep a strong reference to
# `item` until it is resumed or closed.
yield item
Run Code Online (Sandbox Code Playgroud)
这是weakref回调检查防护的地方:
def _remove(item, selfref=ref(self)):
self = selfref()
if self is not None:
if self._iterating:
self._pending_removals.append(item)
else:
self.data.discard(item)
Run Code Online (Sandbox Code Playgroud)
您还可以看到WeakKeyDictionary和中使用的相同防护WeakValueDictionary。
在旧的Python版本(3.0或2.6及更早版本)上,不存在此防护措施。如果您需要支持2.6或更早版本,它看起来像它应该是安全的使用keys,values以及items与弱字典类; 我没有列出WeakSet的选项,因为WeakSet当时不存在。如果3.0上有一个安全,无损的选项,我还没有找到,但是希望没有人需要支持3.0。