Python字典遍历性能优化:700万行文件频次统计慢的问题
问题分析与优化方案
嘿,你的问题我仔细看了——700万行的文件跑几小时确实不太正常,核心问题不在于文件读取方式本身,而是内存爆仓+无意义的IO操作拖慢了整个流程。下面我拆解一下现有代码的坑点,再给你一套高效的优化方案:
现有代码的核心问题
- 一次性加载全量数据到内存:
list(open(file_in, 'r'))直接把700万行读进列表,再做strip处理,这会占用巨量内存,甚至可能触发系统虚拟内存交换(把内存数据写到硬盘上),速度直接暴跌。 - 字典存储冗余数据:你把每个key对应的所有行都存在字典里,700万行的话,字典会大到离谱,遍历的时候缓存根本扛不住,自然越跑越慢。
- 高频次控制台打印:每处理一行就打印一次,700万次的控制台IO是超级耗时的,这绝对是拖慢速度的头号元凶。
- 低效的key判断:
keyf not in dict_in.keys()其实没必要取keys视图,直接keyf not in dict_in更快,因为字典的in操作本身就是O(1)的。
优化思路
- 流式处理,不一次性加载全量数据:逐行读取处理,内存里只存当前行,大幅降低内存占用。
- 分两次遍历文件:第一次只统计每个key的出现次数,不存所有行;第二次根据统计好的次数,把行写到对应文件。这样内存只需要存key和计数,而不是几百万行文本。
- 砍掉不必要的打印:控制台打印速度远慢于内存操作,除非调试,否则完全可以去掉,或者改成每10万行打印一次进度,既不影响速度又能看到进展。
- 用
defaultdict简化计数:比手动判断key是否存在更简洁高效。 - 用
with语句管理文件:自动处理文件打开关闭,避免资源泄漏,代码也更干净。
优化后的代码
from collections import defaultdict def count_key_frequencies(input_file): key_counts = defaultdict(int) # 第一次遍历:只统计每个key的出现次数 with open(input_file, 'r', encoding='utf-8') as f: for line_num, line in enumerate(f, 1): stripped_line = line.strip() if not stripped_line: # 跳过空行,按需调整 continue # 处理行长度不足的情况,避免切片报错 if len(stripped_line) >= 69: keyf = stripped_line[10:69] key_counts[keyf] += 1 # 每10万行打印一次进度,避免频繁IO if line_num % 100000 == 0: print(f"已处理 {line_num} 行") return key_counts def split_lines_by_frequency(input_file, output_single, output_double, output_multi, key_counts): # 第二次遍历:根据计数写入对应文件 with open(input_file, 'r', encoding='utf-8') as in_f, \ open(output_single, 'w', encoding='utf-8') as out1, \ open(output_double, 'w', encoding='utf-8') as out2, \ open(output_multi, 'w', encoding='utf-8') as out3: for line in in_f: stripped_line = line.strip() if not stripped_line: continue if len(stripped_line) >= 69: keyf = stripped_line[10:69] count = key_counts.get(keyf, 0) if count == 1: out1.write(stripped_line + '\n') elif count == 2: out2.write(stripped_line + '\n') elif count > 2: out3.write(stripped_line + '\n') # 主程序入口 if __name__ == "__main__": # 替换成你的实际文件路径 INPUT_FILE = "your_input_file.txt" OUTPUT_1 = "frequency_1.txt" OUTPUT_2 = "frequency_2.txt" OUTPUT_MORE = "frequency_more_than_2.txt" print("开始统计key出现频次...") key_counts = count_key_frequencies(INPUT_FILE) print("开始按频次拆分文件...") split_lines_by_frequency(INPUT_FILE, OUTPUT_1, OUTPUT_2, OUTPUT_MORE, key_counts) print("所有处理完成!")
额外小贴士
- 处理短行情况:我加了
len(stripped_line) >= 69的判断,避免行长度不够时切片报错,你可以根据实际情况调整。 - 指定编码:打开文件时加上
encoding='utf-8'(或你文件的实际编码),避免系统默认编码导致的乱码或读取错误。 - 增大缓冲区:如果想要更快的IO,可以给
open加上buffering=1024*1024(1MB缓冲区),减少磁盘读写次数。 - 极端内存情况:如果key的数量特别多(比如大部分key都是唯一的),可以把第一次统计的结果写入临时文件,第二次遍历时读取临时文件的计数,不过700万行的话,普通字典应该完全能hold住。
这样调整后,内存占用会降到原来的几十分之一,速度至少能提升几十倍,不会再跑几小时了。
内容的提问来源于stack exchange,提问作者onlyf
相关产品推荐
相关产品推荐

