You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义类型对象在序列中可被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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 18:12:43