在 Python 2.7 或 Python 3.1+ 中迭代 、 或 实际上是安全WeakKeyDictionary
的WeakValueDictionary
。早在 2010 年,他们就设置了一个迭代保护,以防止弱引用回调从底层 dict 中删除引用或在迭代期间设置,但文档从未更新。WeakSet
使用警卫,如果一个条目在迭代到达它之前死亡,迭代将跳过该条目,但不会导致段错误或 RuntimeError 或任何东西。死条目将被添加到待删除的列表中并稍后处理。
这是警卫(不是线程安全的,尽管有评论):
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()
这是 WeakKeyDictionary weakref 回调检查守卫的地方:
def remove(k, selfref=ref(self)):
self = selfref()
if self is not None:
if self._iterating:
self._pending_removals.append(k)
else:
del self.data[k]
这里是WeakKeyDictionary.__iter__
设置警卫的地方:
def keys(self):
with _IterationGuard(self):
for wr in self.data:
obj = wr()
if obj is not None:
yield obj
__iter__ = keys
在其他迭代器中使用相同的保护。
如果这个守卫不存在,呼叫list(d.items())
也不安全。GC 传递可能发生在items
迭代器内部,并在迭代期间从 dict 中删除项目。(用 C 编写的事实list
不会提供任何保护。)
回到 2.6 和更早版本,迭代 WeakKeyDictionary 或 WeakValueDictionary 的最安全方法是使用items
. items
将返回一个列表,它将使用底层 dict 的items
方法,该方法(大多数情况下?)不会被 GC 中断。3.0 中 dict API 的变化改变了keys
//的工作方式values
,items
这可能就是当初引入守卫的原因。