自定义JSON编码器未被json.dump调用的问题排查
问题原因与解决方案
为什么json.dump()没触发自定义编码器?
json.dumps()直接调用编码器的encode()方法,但json.dump()默认使用iterencode()的非一次性模式,不会触发你重写的encode()逻辑——这就是两种方法表现不一致的核心原因。
另外你原始代码还有个隐患:在encode()里直接修改了原始字典self.G的键,会导致后续使用该对象时,元组键已经被替换成字符串,产生意外副作用。
解决方案1:修复自定义编码器(支持嵌套结构+无副作用)
重写iterencode()方法强制触发自定义逻辑,同时递归处理所有层级的字典,且不修改原始对象:
from collections.abc import MutableMapping import json import numpy as np class JSONSerializer(json.JSONEncoder): def encode(self, obj): # 递归转换所有字典的元组键,生成新对象不修改原始数据 def transform(o): if isinstance(o, MutableMapping): new_dict = {} for key, value in o.items(): if isinstance(key, tuple): str_key = f"{key[0]}:{key[1]}" new_dict[str_key] = transform(value) else: new_dict[key] = transform(value) return new_dict elif isinstance(o, (list, tuple)): return [transform(item) for item in o] else: # 额外处理numpy浮点数,避免json序列化失败 return float(o) if isinstance(o, np.float64) else o transformed_obj = transform(obj) return super().encode(transformed_obj) def iterencode(self, o, _one_shot=False): # 让dump时的iterencode调用自定义的encode方法 yield self.encode(o) class Agent(object): def __init__(self, states, alpha=0.15, random_factor=0.2): self.state_history = [((0, 0), 0)] # state, reward self.alpha = alpha self.random_factor = random_factor self.G = {} self.init_reward(states) def init_reward(self, states): for i, row in enumerate(states): for j, col in enumerate(row): self.G[(j,i)] = np.random.uniform(high=1.0, low=0.1) def memorize(self): with open("memory.json", "w") as w: json.dump(self.G, w, cls=JSONSerializer) if __name__ == "__main__": robot = Agent(states=np.zeros((6, 6)), alpha=0.1, random_factor=0.25) print(robot.G) robot.memorize()
解决方案2:直接转换字典(更简洁)
如果只有顶层字典的键是元组,不需要自定义编码器,直接生成转换后的字典再序列化:
# 修改Agent类的memorize方法即可 def memorize(self): # 把元组键转成字符串,同时转换numpy浮点数为原生浮点数 converted_G = {f"{k[0]}:{k[1]}": float(v) for k, v in self.G.items()} with open("memory.json", "w") as w: json.dump(converted_G, w)
内容的提问来源于stack exchange,提问作者Chairos
相关产品推荐
相关产品推荐

