含自定义对象的大文件转Python字典内存及JSON序列化问题
解决大体积自定义格式数据解析的问题
针对你遇到的eval内存溢出、JSON序列化失败以及大文件打印缓慢的问题,给出以下实用方案:
1. 替换eval,用安全高效的ast.literal_eval解析
eval会执行任意代码,且处理大文件时会一次性加载所有内容并创建大量中间对象,导致内存暴涨。改用ast.literal_eval更安全,内存效率也更高,但需要先把自定义类型转成Python原生字面量:
import ast import re with open('transpostion.txt', 'r') as f: raw_data = f.read() # 把Cp(+57)转成('Cp', 57)格式 raw_data = re.sub(r'Cp\(([+-]\d+)\)', r"('Cp', \1)", raw_data) # 把PovScore(Cp(...), BLACK)转成('PovScore', ('Cp', 57), 'BLACK') raw_data = re.sub(r'PovScore\((.*?), (BLACK|WHITE)\)', r"('PovScore', \1, \2)", raw_data) # 把BLACK/WHITE转成字符串字面量 raw_data = raw_data.replace('BLACK', "'BLACK'").replace('WHITE', "'WHITE'") # 解析为字典 data_dict = ast.literal_eval(raw_data)
解析完成后,你可以遍历字典,把元组重新转换回PovScore和Cp对象。
2. 让JSON支持自定义对象序列化/反序列化
JSON默认不支持自定义类对象,需要自定义编码器和解码器来处理PovScore、Cp和BLACK/WHITE:
序列化(保存数据时用)
import json class ChessScoreEncoder(json.JSONEncoder): def default(self, obj): # 处理PovScore对象 if isinstance(obj, PovScore): return { '__type__': 'PovScore', 'cp': obj.cp, # 假设PovScore有cp属性存储Cp对象 'color': obj.color # 假设color是BLACK/WHITE枚举 } # 处理Cp对象 elif isinstance(obj, Cp): return { '__type__': 'Cp', 'value': obj.value # 假设Cp有value属性存储分数 } # 处理BLACK/WHITE枚举 elif obj in (BLACK, WHITE): return obj.name # 假设枚举有name属性,比如BLACK.name = 'BLACK' # 其他类型用默认处理 return super().default(obj) # 保存为JSON文件 with open('transpostion.json', 'w') as f: json.dump(data_dict, f, cls=ChessScoreEncoder, indent=2)
反序列化(读取JSON文件时用)
def chess_score_decoder(obj): if '__type__' in obj: # 还原PovScore对象 if obj['__type__'] == 'PovScore': cp_obj = Cp(obj['cp']['value']) return PovScore(cp_obj, obj['color']) # 还原Cp对象 elif obj['__type__'] == 'Cp': return Cp(obj['value']) # 还原BLACK/WHITE枚举 elif obj['__type__'] == 'BLACK': return BLACK elif obj['__type__'] == 'WHITE': return WHITE return obj # 读取JSON文件 with open('transpostion.json', 'r') as f: data_dict = json.load(f, object_hook=chess_score_decoder)
3. 优化大文件读取与打印
- 逐行读取大文件:避免一次性加载全部内容到内存,减轻内存压力:
import ast import re # 匹配每一行的键值对 line_pattern = re.compile(r"'(0x[0-9a-f]+)': \[(.*?), (\d+)\]") data_dict = {} with open('transpostion.txt', 'r') as f: for line in f: line = line.strip().rstrip(',').strip() if not line: continue match = line_pattern.match(line) if match: key = match.group(1) score_raw = match.group(2) num = int(match.group(3)) # 处理分数部分为字面量 score_raw = re.sub(r'Cp\(([+-]\d+)\)', r"('Cp', \1)", score_raw) score_raw = re.sub(r'PovScore\((.*?), (BLACK|WHITE)\)', r"('PovScore', \1, \2)", score_raw) score_raw = score_raw.replace('BLACK', "'BLACK'").replace('WHITE', "'WHITE'") score = ast.literal_eval(score_raw) data_dict[key] = [score, num]
- 分批打印大字典:直接打印整个大字典会导致IDLE卡顿,只打印部分关键信息:
# 打印前10条数据 for idx, (key, value) in enumerate(data_dict.items()): if idx >= 10: break print(f"{key}: {value}") # 打印总条数 print(f"... 总计 {len(data_dict)} 条数据")
内容的提问来源于stack exchange,提问作者winapiadmin
相关产品推荐
相关产品推荐

