gpt4 book ai didi

python - 安全地迭代 WeakKeyDictionary 和 WeakValueDictionary

转载 作者:太空狗 更新时间:2023-10-29 20:50:22 25 4
gpt4 key购买 nike

Python 3.2weakref 模块的 WeakKeyDictionaryWeakValueDictionary 的文档有关于迭代这些的说明容器:

Note: Caution: Because a WeakKeyDictionary is built on top of a Python dictionary, it must not change size when iterating over it. This can be difficult to ensure for a WeakKeyDictionary because actions performed by the program during iteration may cause items in the dictionary to vanish “by magic” (as a side effect of garbage collection).

作为这些容器行为的规范,这似乎相当可怕。尤其是在运行使用 CPython 垃圾收集器的代码(使用包含循环的数据结构时)或使用其他 Python 实现(例如 Jython)时,听起来好像没有安全的方法来迭代这些集合。

当垃圾收集器可能在我的程序中的任何时候清除引用时,我如何安全地迭代这些集合?为 CPython 提供解决方案是我的首要任务,但我也对其他实现的问题感兴趣。

这可能是一种遍历 WeakKeyDictionary 的安全方法吗?

import weakref

d = weakref.WeakKeyDictionary()

...

for k, v in list(d.items()):
...

最佳答案

在 Python 2.7 或 Python 3.1+ 中迭代 WeakKeyDictionaryWeakValueDictionaryWeakSet 实际上是安全的。 They put in an iteration guard这可以防止 weakref 回调从底层 dict 中删除引用或在 2010 年的迭代期间设置,但文档从未更新过。

有了守卫,如果一个条目在迭代到达它之前死亡,迭代将跳过该条目,但它不会导致段错误或运行时错误或任何东西。死条目将被添加到待删除列表中并在以后处理。

Here's the guard (不是线程安全的,尽管有评论):

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()

Here's where the WeakKeyDictionary weakref callback checks the guard :

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]

And here's where WeakKeyDictionary.__iter__ sets the guard :

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 中删除项目。 (list 是用 C 语言编写的,这一事实不会提供任何保护。)


回到 2.6 和更早版本,迭代 WeakKeyDictionary 或 WeakValueDictionary 的最安全方法是使用 itemsitems 会返回一个列表,它会使用底层字典的 items 方法,该方法(大部分?)不会被 GC 中断。 3.0 中的 dict API 更改改变了 keys/values/items 的工作方式,这可能就是引入守卫的原因。

关于python - 安全地迭代 WeakKeyDictionary 和 WeakValueDictionary,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12428026/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com