自定义类型对象在序列中可被in检测,字典中为何失效?
为什么字典的
in运算符不符合官方文档描述的逻辑? Python官方文档指出:对于list、tuple、set、frozenset、dict或collections.deque等容器类型,表达式
x in y等价于any(x is e or x == e for e in y)。
但实际测试中却出现了矛盾:
列表测试(符合预期)
class Val: def __init__(self, val): self.val = val def __eq__(self, other): return self.val == other.val __hash__ = object.__hash__ one = Val(1) ; two = Val(2) ; three = Val(3) valSeq = [one, two, three] print("Val(2) in valSeq:\n ", Val(2) in valSeq)
输出:True,和文档描述的逻辑一致。
字典测试(不符合预期)
valMap = {one: "ekam", two: "dve", three: "trini"} print("valMap[Val(2)]:") try: print(valMap[Val(2)]) except KeyError: print(" KeyError") print() print("Val(2) in valMap:\n ", Val(2) in valMap)
输出:
valMap[Val(2)]: KeyError Val(2) in valMap: False
自定义测试函数(返回True)
def inTest(obj, seq): return any(obj is test or obj == test for test in seq) print("inTest(Val(2), valMap):\n ", inTest(Val(2), valMap))
返回True,说明Val(2)和字典中的two对象逻辑上相等,但in运算符却不生效。
核心原因:字典依赖哈希表快速查找,而非全量遍历
字典(dict)和列表、元组这类线性容器的底层实现完全不同:
- 线性容器(list/tuple等):
x in y会逐个遍历元素,用is或==判断匹配,和文档描述的any(...)逻辑完全一致。 - 哈希表容器(dict/set等):
in运算符(以及键查找)会先计算x的哈希值,直接定位到哈希表对应槽位;只有槽位存在元素时,才会用==判断是否匹配。
你的Val类违反了Python的核心规则:如果两个对象a == b为True,则它们的hash(a)必须等于hash(b)。你直接复用了object.__hash__,这会让每个Val实例的哈希值基于内存地址生成——Val(2)和two虽然==为True,但哈希值完全不同,导致字典无法定位到正确的槽位,自然返回False。
修复方法
给Val类实现与__eq__匹配的__hash__方法,让相等的对象拥有相同哈希值:
class Val: def __init__(self, val): self.val = val def __eq__(self, other): return isinstance(other, Val) and self.val == other.val def __hash__(self): return hash(self.val) # 基于val的哈希值生成,保证相等对象哈希一致
修改后,Val(2) in valMap会返回True,valMap[Val(2)]也能正确取出对应值。
补充说明
官方文档的描述是逻辑等价,而非实现等价。哈希表容器为了性能,不会遍历所有元素,但最终的匹配逻辑(找到相等元素)和any(...)一致——前提是你遵守了哈希与相等性的绑定规则。
内容的提问来源于stack exchange,提问作者jamadagni
相关产品推荐
相关产品推荐

