运行时错误:在默认字典的迭代中,字典大小发生变化

6 投票
3 回答
9558 浏览
提问于 2025-04-17 09:41

在这里回答一个特定问题时,我遇到了一个奇怪的问题,自己却说不清楚。可惜的是,谷歌搜索的前两页结果中,有一个SO页面也没有帮助。

问题代码

>>> somedata=[random.randint(1,1000) for i in xrange(1,10000)]
>>> somehash=collections.defaultdict(int)
>>> for d in somedata:
    somehash[d]+=1      
>>> maxkey=0
>>> for k,v in somehash.iteritems():
    if somehash[maxkey] > v:
        maxkey=k            

Traceback (most recent call last):
  File "<pyshell#700>", line 1, in <module>
    for k,v in somehash.iteritems():
RuntimeError: dictionary changed size during iteration
>>> for k,v in somehash.iteritems():
    if somehash[maxkey] > v:
        maxkey=k
>>>

由于某种奇怪的原因,第一次我遍历字典时,Python出现了问题,但后面的执行就没问题了。就像例子中显示的,第一次遍历字典时,它给了我一个运行时错误,但下次就没有再抱怨了。

你知道可能出了什么问题吗?

如果需要的话,这里还有一些信息

>>> sys.version_info
sys.version_info(major=2, minor=7, micro=0, releaselevel='final', serial=0)
>>> sys.version
'2.7 (r27:82525, Jul  4 2010, 09:01:59) [MSC v.1500 32 bit (Intel)]'

OS: Microsoft Windows [Version 6.1.7601] (Windows 7)

3 个回答

1

你正在生成9999个(有点)随机的整数,这些整数的范围在1到1000之间,存储在somedata里。然后你用这些整数作为somehash的键,来记录这些数字在中出现的次数。

因为maxkey=0,所以这个键永远不会存在。你使用的是一个叫做defaultdict的东西,当每个键第一次出现时,会自动创建一个条目,使用default_factory函数返回一个空列表,因此在迭代时会正确地抛出错误,正如*FastTurtle*已经指出的那样。

你可以使用get方法来安全地获取一个项目。

import random
import collections

somedata=[random.randint(1,1000) for i in xrange(1,10000)]
somehash=collections.defaultdict(int)
for d in somedata:
   somehash[d]+=1 

maxkey=0
for k,v in somehash.iteritems():
   if somehash.get(maxkey) > v:
       maxkey=k 
       print k,v  

我看到你在使用Python 2.7,它有一个新的集合类叫Counter,用于计数可哈希的对象。使用Counter应该比上面的代码更快,而且可以把你的代码简化为:

somedata=[random.randint(1,1000) for i in xrange(1,10000)]
somehash=collections.Counter(somedata)
6

正如Sven所解释的,你遇到的错误是因为defaultdict的工作方式。当你在defaultdict中查找一个键时,如果这个键不存在,它会自动获取一个默认值(这就是它名字的由来),并把这个键和默认值一起添加到字典里。这就是你出现RuntimeError的原因。

为了避免这个问题,你可以这样做:

for k, v in somehash.items():
    if somehash[maxkey] > v:
        maxkey = k

主要的区别在于,somehash.items()返回的是一个包含(键,值)对的列表,所以你实际上是在遍历这个列表,而不是在遍历somehash本身。.keys().iterkeys()也是一样的道理。

13

在遍历字典的时候,如果你同时添加或删除字典里的项目,那就是个错误。因为somehash是一个defaultdict,所以在下面这行代码中,虽然看起来只是读取数据

if somehash[maxkey] > k:

但实际上可能会添加一个新的键,这就会导致你遇到的错误。

撰写回答