【发布时间】:2017-04-18 18:08:36
【问题描述】:
在迭代时删除一个项目通常会导致RuntimeError: dictionary changed size during iteration异常:
d = {1: 2}
# exception raised
for k in d:
del d[k]
更准确地说,删除本身会成功。但是,要进入下一轮迭代,解释器必须调用next(it),其中it 是通过它之前获得的字典的迭代器。此时,next() 会注意到字典大小发生了变化,并抱怨。
到目前为止一切顺利。但是,如果我们同时删除和添加一个项目到字典会怎样:
d = {1: 1}
# no exception raised
for k in d:
# order of next two lines doesn't matter
d[k*10] = k*10
del d[k]
我几乎可以肯定这是不安全的(文档暗示在迭代期间既不允许插入也不允许删除)。为什么解释器允许这段代码正常运行?
我唯一的猜测是,每当调用插入或删除方法时,检查哪些迭代器无效的成本太高了。所以dict 并没有试图完美地提出这个异常。相反,它只是跟踪每个迭代器中字典的大小,并在迭代器实际被要求移动到下一项时检查它是否没有改变。有没有能够以低成本实现全面验证的方法?
【问题讨论】:
-
您是在寻找使您的循环更健壮的东西,还是想讨论 Python 的实现细节?
-
看起来您希望字典键在循环中不可变。我不认为这是可行的。
-
@KlausD。嗯,我猜两者都有?如果有一种技术可以做到这一点,我会考虑自己使用它。但为了了解它的成本(运行时间、代码复杂性等),了解 CPython 为什么不使用它对我来说很重要。
-
@DYZ 不可变键比我问的要强大得多。我不想仅仅因为给定键的值被更改而引发异常 - 在循环中这样做是完全安全的(事实上,如果它不是 python 将是一种损坏的语言!)
-
@DYZ Btw,制作不可变版本的
dict可作为 python 中的内置功能使用:immutable_d = types.MappingProxyType(d)。如果根本不需要任何修改,则在循环中使用它是安全的。当然,仍然可以通过使用原始的(可变的)d来搞砸。无论如何,这是我需要的更严格的限制。
标签: python python-3.x dictionary python-internals