使对象x成为[x]"中的" x返回False

时间:2015-04-17 06:46:16

标签: python python-internals

如果我们制作这样的病态马铃薯:

>>> 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__迭代,但在检查相等性之前它是checking identity。既然身份并不意味着相等(例如参见NaN对象),那么列表在身份比较中短路的原因是什么?

2 个答案:

答案 0 :(得分:11)

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

不幸的是,dict s,set和朋友通过哈希进行操作,所以如果你搞砸了那些,你确实可以有效地打破它们。

有关某些历史记录,请参阅this issuethis issue

答案 1 :(得分:8)

一般而言,打破身份意味着平等的假设可以打破Python中的各种事物。确实NaN打破了这个假设,因此NaN在Python中打破了一些东西。讨论可以在this Python bug中找到。在Python 3.0的预发布版本中,删除了对此假设的依赖,但该错误的解决方案是将其重新放入(即,使Python 3提供与Python 2相同的行为,其中身份检查快捷方式是完成)。 Python 3的documentation正确地说:

  

对于容器类型,例如list,tuple,set,frozenset,dict或collections.deque,表达式x in y等同于any(x is e or x == e for e in y)

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

  

对于列表和元组类型,当且仅当存在索引i使得x == y [i]为真时,y中的x才为真。

如果你愿意,你可以提出一个关于这个问题的文档错误,虽然这是一个非常深奥的问题,所以我怀疑它会在任何人的优先级列表上都很高。

相关问题