嵌套字典迭代报错:构建双向通话时长字典求助
解决字典迭代时修改大小引发的RuntimeError问题
问题描述
现有嵌套字典,外层键为主叫手机号,内层键为被叫手机号,值为通话时长(秒)。需要将其转换为双向完整通话字典:所有出现的号码作为外层键,对应内层键为通话对象,值为累计通话时长,已有记录则累加时长。尝试的代码触发了RuntimeError: dictionary changed size during iteration,需提供可行实现方法。
原始字典
call_dict = { '123456789': {'132456789': 583, '123546789': 444, '257918896': 528, '111111111': 495}, '296358741': {'132456789': 631, '123456789': 98, '456789123': 395}, 111111111: {'123456789': 150} }
注:避免使用
dict作为变量名,这会覆盖Python内置的dict类型
期望结果
result_dict = { '123456789': {'132456789': 583, '123546789': 444, '257918896': 528, '111111111': 645, '296358741': 98}, '296358741': {'132456789': 631, '123456789': 98, '456789123': 395}, '132456789': {'123456789': 583, '296358741': 631}, '111111111': {'123456789': 645}, '456789123': {'296358741': 395}, '123546789': {'123456789': 444}, '257918896': {'123456789': 528} }
注:将
495+150替换为实际累加结果645,同时去除了重复的'296358741'键
错误原因分析
原代码直接迭代原字典for k in dict,在迭代过程中通过dict[called] = ...给字典添加新键,这会改变字典的大小。Python中字典在迭代时不允许修改其结构(增删键),因此触发RuntimeError: dictionary changed size during iteration。
可行解决方案
方案1:迭代原字典键的副本
通过list(dict.keys())获取原字典所有键的列表副本,迭代这个固定的列表,即使原字典后续新增键,也不会影响迭代过程。
修改后的代码:
# 重命名变量避免覆盖内置dict call_dict = { '123456789': {'132456789': 583, '123546789': 444, '257918896': 528, '111111111': 495}, '296358741': {'132456789': 631, '123456789': 98, '456789123': 395}, 111111111: {'123456789': 150} } # 迭代键的列表副本 for caller in list(call_dict.keys()): # 统一键的类型为字符串,避免数字和字符串键重复(比如111111111和'111111111') caller_str = str(caller) # 确保主叫号码在字典中是字符串键 if caller != caller_str: call_dict[caller_str] = call_dict.pop(caller) caller = caller_str called_dict = call_dict[caller] for called, duration in called_dict.items(): called_str = str(called) # 处理被叫号码的反向记录 if called_str not in call_dict: call_dict[called_str] = {caller: duration} else: if caller in call_dict[called_str]: call_dict[called_str][caller] += duration else: call_dict[called_str][caller] = duration print(call_dict)
额外优化:统一所有号码为字符串类型,避免出现数字键和字符串键重复的问题(原字典中存在
111111111数字键和'111111111'字符串键)
方案2:创建新字典存储结果
完全基于原字典的数据构建新字典,避免修改原字典,从根源上避免迭代时修改字典的问题。
代码实现:
call_dict = { '123456789': {'132456789': 583, '123546789': 444, '257918896': 528, '111111111': 495}, '296358741': {'132456789': 631, '123456789': 98, '456789123': 395}, 111111111: {'123456789': 150} } result = {} # 先处理所有主叫到被叫的记录 for caller, called_dict in call_dict.items(): caller_str = str(caller) # 初始化主叫的条目 if caller_str not in result: result[caller_str] = {} # 添加主叫到被叫的时长 for called, duration in called_dict.items(): called_str = str(called) result[caller_str][called_str] = result[caller_str].get(called_str, 0) + duration # 处理被叫到主叫的反向记录 if called_str not in result: result[called_str] = {} result[called_str][caller_str] = result[called_str].get(caller_str, 0) + duration print(result)
这种方法逻辑更清晰,也不会出现迭代时修改字典的问题,同时自动统一了号码的字符串类型。
内容的提问来源于stack exchange,提问作者clumsyAlien
相关产品推荐
相关产品推荐

