如果我们制作这样的病态土豆:
>>> class Potato:
... def __eq__(self, other):
... return False
... def __hash__(self):
... return random.randint(1, 10000)
...
>>> p = Potato()
>>> p == p
False
我们可以用这种方式破坏集合和字典(注意:即使
__eq__
返回True
,它也是一样的,它与破坏它们的散列混在一起):
>>> p in {p}
False
>>> p in {p: 0}
False
还有
len({p: 0, p: 0}) == 2
,并且 {p: 0}[p]
会引发 KeyError,基本上所有与映射相关的内容都会如预期的那样消失。
但我没想到的是,我们不能打破列表
>>> p in [p]
True
这是为什么呢? 看起来
list.__contains__
会迭代,但首先是 检查身份,然后再检查相等性。 既然身份并不意味着相等(例如参见 NaN 对象),那么身份比较时列表短路的原因是什么?
一般来说,打破恒等意味着相等的假设可能会破坏 Python 中的很多事情。 确实,NaN 打破了这个假设,因此 NaN 打破了 Python 中的一些东西。 讨论可以在this Python bug中找到。在 Python 3.0 的预发行版本中,删除了对这一假设的依赖,但解决该错误的方法是将其放回原处(即,使 Python 3 提供与 Python 2 相同的行为,其中身份检查快捷方式为完毕)。 Python 3 的文档正确地说:
对于列表、元组、集合、frozenset、dict 或 collections.deque 等容器类型,表达式
相当于x in y
。any(x is e or x == e for e in y)
但是,Python 2 的文档似乎不正确,因为它说:
对于列表和元组类型,当且仅当存在索引 i 使得 x == y[i] 为 true 时,x in y 为 true。
如果你愿意,你可以提出一个关于此的文档错误,尽管这是一个非常深奥的问题,所以我怀疑它会在任何人的优先列表中占据很高的位置。