使对象 x 使得 "x in [x]" returns False

Making an object x such that "x in [x]" returns False

如果我们做出这样的病态土豆:

>>> class Potato:
...     def __eq__(self, other):
...         return False
...     def __hash__(self):
...         return random.randint(1, 10000)
... 
>>> p = Potato()
>>> p == p
False

我们可以这样打断sets和dicts(注意:即使__eq__returnsTrue也是一样的破坏它们的哈希):

>>> p in {p}
False
>>> p in {p: 0}
False

还有 len({p: 0, p: 0}) == 2{p: 0}[p] 引发 KeyError,基本上所有与映射相关的东西都会按预期退出 window。

但是我没想到的是我们不能打破列表

>>> p in [p]
True

这是为什么?似乎 list.__contains__ 迭代,但它是第一个 checking identity 在检查相等性之前。由于身份并非意味着相等(例如参见 NaN 对象),列表在身份比较中短路的原因是什么?

listtuple 等确实在相等性检查之前进行身份检查,这种行为的动机是 these invariants:

assert a in [a]
assert a in (a,)
assert [a].count(a) == 1
for a in container:
    assert a in container    # this should ALWAYS be true

不幸的是,dicts、sets 和朋友们通过哈希操作,所以如果你弄乱了它们,你确实可以有效地破坏它们。

有关历史,请参阅 this issue and this issue

一般来说,打破身份意味着平等的假设可以打破 Python 中的各种事物。 NaN 确实打破了这个假设,因此 NaN 打破了 Python 中的一些东西。讨论可以在this Python bug. In a pre-release version of Python 3.0, reliance on this assumption was removed, but the resolution of the bug was to put it back in (i.e., make Python 3 give the same behavior as Python 2, in which the identity check shortcut is done). The documentation中找到Python 3正确地说:

For container types such as list, tuple, set, frozenset, dict, or collections.deque, the expression x in y is equivalent to any(x is e or x == e for e in y).

但是,Python 2 的文档似乎不正确,因为它说:

For the list and tuple types, x in y is true if and only if there exists an index i such that x == y[i] is true.

如果你愿意,你可以提出一个关于这个的文档错误,尽管这是一个非常深奥的问题,所以我怀疑它会在任何人的优先列表中排在前列。