Safely iterating over WeakKeyDictionary and WeakValueDictionary 问题并没有像我希望的那样让我放心,而且它已经足够老了,值得再次提问而不是发表评论。
假设我有一个可散列的 MyHashable
类,我想构建一个 WeakSet
:
obj1 = MyHashable()
obj2 = MyHashable()
obj3 = MyHashable()
obj2.cycle_sibling = obj3
obj3.cycle_sibling = obj2
ws = WeakSet([obj1, obj2, obj3])
然后我删除了一些局部变量,并转换为一个列表,为后面的循环做准备:
del obj2
del obj3
list_remaining = list(ws)
我引用的问题似乎声称这很好,但即使没有任何类型的显式
for
循环,我是否已经冒着循环垃圾收集器在 list_remaining
的构造函数期间启动并更改集合大小的风险?我希望这个问题非常罕见,以至于很难通过实验检测到,但可能会使我的程序崩溃一次。我什至不觉得那个帖子上的各种评论者真的达成了共识
for obj in list(ws):
...
没问题,但他们似乎都认为
list(ws)
本身可以一直运行而不会崩溃,我什至不相信这一点。 list
构造函数是否以某种方式避免使用迭代器,从而不关心设置大小的变化?由于 list
是内置的,所以在 list
构造函数期间不会发生垃圾收集吗?目前我已经编写了我的代码来破坏性地从
pop
中删除 WeakSet
项目,从而完全避免了迭代器。我不介意破坏性地这样做,因为在我的代码中我已经完成了 WeakSet
。但我不知道我是不是偏执狂。 最佳答案
文档令人沮丧地缺乏这方面的信息,但是查看 implementation ,我们可以看到 WeakSet.__iter__
可以防止此类问题。
在对 WeakSet
进行迭代期间,weakref 回调将添加对挂起删除列表的引用,而不是直接从底层集合中删除引用。如果一个元素在迭代到达之前死亡,迭代器不会产生该元素,但你不会得到段错误或 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()
这是
__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
这里是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)
您还可以看到
WeakKeyDictionary
和 WeakValueDictionary
中使用的相同保护。在旧的 Python 版本(3.0 或 2.6 及更早版本)上,此防护不存在。如果您需要支持 2.6 或更早版本,看起来将
keys
、 values
和 items
与弱 dict 类一起使用应该是安全的;我没有列出 WeakSet 的选项,因为当时 WeakSet 不存在。如果在 3.0 上有安全、无损的选项,我还没有找到,但希望没有人需要支持 3.0。关于python - 将 python WeakSet 提供给列表构造函数是否安全?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/52693294/