自定义__eq__和__hash__后,Python字典为何出现重复键?
Q表出现重复键的原因及修复方案
你遇到的问题根源在于State对象的data属性是可变的列表,且这些列表在State被作为字典键存入后仍被修改,导致字典无法正确识别原本应该相等的键。
具体分析
- 你的
ToyMDP中,每次创建State时传入的是self.toy.buttons(一个列表),这个列表的引用会被存在State.data中。 - 在
Toy.press方法中,会直接修改当前self.toy.buttons列表的元素(self.buttons[button] = 1 - self.buttons[button]),这会导致已经创建的State对象的data内容被意外修改。 - 当
State对象作为键存入q_func后,它的哈希值(依赖于data内容)和相等性判断会发生变化。字典在存入时是根据当时的哈希值分配bucket的,后续哈希值变化后,字典无法将修改后的State与之前存入的键关联,导致新的相等键被当成新条目存入,最终出现大量重复。
修复方案
方案1:使用不可变对象作为State的data
将传入State的列表转换为不可变的tuple,确保State对象的哈希和相等性不会后续变化:
- 修改
ToyMDP.__init__中的初始状态创建:
init_state = State(data=tuple(self.toy.buttons))
- 修改
ToyMDP._transition_func中的新状态创建:
new_state = State(tuple(self.toy.buttons))
方案2:修改State类确保data不可变
如果无法修改MDP代码,可以在State类的初始化中自动将可变序列转为不可变类型:
class State: def __init__(self, data=[]): # 将列表转为元组,确保不可变 if isinstance(data, list): self.data = tuple(data) elif type(data).__module__ == np.__name__: # 对于numpy数组,转为扁平化元组 self.data = tuple(data.flatten()) else: self.data = data # 保留原有的__eq__和__hash__方法 def __eq__(self, other): if isinstance(other, State): return self.data == other.data return False def __hash__(self): if type(self.data).__module__ == np.__name__: return hash(str(self.data)) elif self.data.__hash__ is None: return hash(tuple(self.data)) else: return hash(self.data)
额外修复:完善MDP的reset方法
你的ToyMDP没有重写reset方法,导致每次episode结束后self.toy的状态不会重置,可能引发其他逻辑问题。添加reset方法:
class ToyMDP(MDP): # ... 其他已有代码 ... def reset(self): self.toy = Toy() self.init_state = State(data=tuple(self.toy.buttons)) self.set_current_state(self.init_state)
内容的提问来源于stack exchange,提问作者selroh18
相关产品推荐
相关产品推荐

