如何解决这个Python错误? RuntimeError: dictionary changed size during iteration

6

它给我这个错误:

Exception in thread Thread-163:
Traceback (most recent call last):
  File "C:\Python26\lib\threading.py", line 532, in __bootstrap_inner
    self.run()
  File "C:\Python26\lib\threading.py", line 736, in run
    self.function(*self.args, **self.kwargs)
  File "C:\Users\Public\SoundLog\Code\Código Python\SoundLog\SoundLog.py", line 337, in getInfo
    self.data1 = copy.deepcopy(Auxiliar.DataCollection.getInfo(1))
  File "C:\Python26\lib\copy.py", line 162, in deepcopy
    y = copier(x, memo)
  File "C:\Python26\lib\copy.py", line 254, in _deepcopy_dict
    for key, value in x.iteritems():
RuntimeError: dictionary changed size during iteration

在执行我的Python程序时,我该如何避免这种情况发生?

提前感谢您的帮助 ;)

3个回答

15
作为其他答案所建议的正常做法,应该避免使用 iteritems(改用 items)。但在你的情况下,这当然不是一个选项,因为 iteritems 调用是在系统调用的深处代表你完成的。
因此,我建议你假设 Auxiliar.DataCollection.getInfo(1) 返回一个字典(在复制过程中发生更改的字典),并将你的 deepcopy 调用更改为:
self.data1 = copy.deepcopy(dict(Auxiliar.DataCollection.getInfo(1)))

这将对所涉及的字典进行"快照",且该快照不会改变,所以您将不会有问题。

如果 Auxiliar.DataCollection.getInfo(1) 不返回一个字典,而是返回一个更复杂的对象,其中包括作为项和/或属性的字典,那么情况就会略微复杂一些,因为您需要对这些字典进行快照。但是,由于您没有给出组成关键的 Auxiliar.DataCollection.getInfo(1) 调用的代码的任何线索,所以在这种情况下无法提供更具体的信息!:-)


1
是的,它是一个字典,你说的确实有效!感谢您快速而好的回答 :) - aF.

2
尽管这个线程已经将近两年了,但我遇到了类似的问题:
我有一个基于队列模块的生产者/消费者系统。我的工作类的run方法被定义为:
def run(self):
    while True:
    a, b, c = Worker._Queue.get()
    # do some stuff
    ...
    self.notify() # notify observers
    Worker._Queue.task_done()

主类定义了一个update方法,用于通知工作线程收集数据并将其存储在字典中。由于多个线程可能会更改主类中的字典,因此这个“临界区域”被锁定。
def update(self, worker):
    Main.indexUpdateLock.acquire()
    # get results of worker
    index = worker.getIndex()
    # copy workers index into the main index
    try:
        for i in index:
            if i in self._index:
                self._index[i] += index[i]
            else:
                self._index[i] = index[i]
    finally:
        # index copied - release the lock
        Main.indexUpdateLock.release()

现在这个程序在大多数情况下都可以正常工作,但是有时候Main的update方法中的'for i in index:'会抛出一个RuntimeError:dictionary changed size during iteration。indexUpdateLock被定义为threading.Lock()或者threading.RLock() - 无论我如何定义它,行为都不会改变。
使用'for i in dict(index):'可以解决问题,但是由于index可能包含几千个条目,复制它并不会真正提高性能 - 这就是为什么我试图直接复制那些值。
虽然update是在Main中定义的,但是通过在worker线程中调用notify(),update也应该在worker线程中执行,因此只有在notify()或稍后的update()完成处理后才会执行task_done()。并且通过定义关键部分,只允许一个线程同时执行此区域 - 或者我在这里有一些逻辑错误吗?我真的看不出来worker的index发生了什么变化,因为对index的唯一访问是在Main.update()和Worker中,但在task_done()没有执行之前,没有其他方法在Worker内修改index。
编辑: 好的,已经解决了问题,问题是由Worker内部的HTMLParser引起的,它发送了一个额外的条目,尽管源已经关闭 - 虽然奇怪的行为。虽然'for i in index:'仍然会产生错误,但是'for i in index.keys():'不会产生错误,因此我将坚持使用它。

0
听起来你正在向你尝试迭代的字典中添加或删除一些内容。大多数编程语言都不允许这样做。

当我使用copy.deepcopy时,它不会创建一个新的字典? - aF.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接