创建一个对象 x,使得 `x in [x]` 返回 False

问题描述 投票:0回答:2

如果我们制作这样的病态土豆:

>>> 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 python-internals
2个回答
12
投票

list
tuple
等,确实在相等性检查之前进行身份检查,并且这种行为是由这些不变量

驱动的
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

不幸的是,

dict
set
和朋友通过哈希进行操作,所以如果你搞乱它们,你确实可以有效地破坏它们。

请参阅本期本期了解一些历史记录。


8
投票

一般来说,打破恒等意味着相等的假设可能会破坏 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。

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

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.