Python 使对象x为;[x]中的x“;返回False

Python 使对象x为;[x]中的x“;返回False,python,python-internals,Python,Python Internals,如果我们做这样一个病态土豆: >>> class Potato: ... def __eq__(self, other): ... return False ... def __hash__(self): ... return random.randint(1, 10000) ... >>> p = Potato() >>> p == p False 我们可以用这种方式分解集合和dicts(

如果我们做这样一个病态土豆:

>>> class Potato:
...     def __eq__(self, other):
...         return False
...     def __hash__(self):
...         return random.randint(1, 10000)
... 
>>> p = Potato()
>>> p == p
False
我们可以用这种方式分解集合和dicts(注意:即使
\uuuueq\uuuu
返回
True
,也一样,这是破坏它们的散列):

而且
len({p:0,p:0})==2
,并且
{p:0}[p]
引发了KeyError,基本上所有与映射相关的内容都会像预期的那样被弹出窗口

但我没想到的是我们不能打破名单

>>> p in [p]
True

为什么呢?似乎
list.\u_包含\u_
迭代,但它是在检查相等性之前的第一个。由于标识并不意味着相等(例如,请参见NaN对象),列表在标识比较时短路的原因是什么?

列表
元组
等确实在相等检查之前进行了标识检查,这种行为的动机是:

不幸的是,
dict
s、
set
s和friends都是通过散列操作的,所以如果你把它们搞乱了,你确实可以有效地打破它们


请参阅和了解一些历史。

一般来说,打破身份意味着平等的假设可能会破坏Python中的许多东西。的确,NaN打破了这个假设,因此NaN打破了Python中的一些东西。有关讨论,请参阅。在Python3.0的预发布版本中,对这一假设的依赖已经消除,但问题的解决办法是将其放回去(即,使Python3提供与Python2相同的行为,在Python2中完成身份检查快捷方式)。Python 3的版本正确地说:

对于容器类型,如list、tuple、set、frozenset、dict或collections.deque,表达式
y中的x
相当于
any(x是e或x==e表示y中的e)

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

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


如果您愿意,您可能会提出一个文档错误,尽管这是一个相当深奥的问题,所以我怀疑它是否会在任何人的优先级列表中占据很高的位置。

可能
列表。uu包含\uu
通过
id()
而不是
eq()
比较对象<代码>(id(p)=id(p))是真的@jornsharpe OP已经知道了。我想他想理解为什么列表首先检查对象标识而不是相等,我想。@HåkenLid是的,它就是这么做的,我想OP是在问为什么。@wim per由BrenBarn链接的bug注释很好地总结了逻辑原因。问题是,标识应该意味着相等。NaN对象之所以这样做,不仅仅是因为标准中说的是Syu,但无论如何,创建一个不遵循该含义的类型是一个非常糟糕的主意。如果你这样做了,你应该1)知道你真的不能这样做,2)准备破解一些代码。3.x中的文档作为OP文档,例如
set
,难道不是错误的吗?这检查的是散列,而不是身份。@jonrsharpe:没错。我关注的是列表/元组案例的异常行为。不过,使用这样的随机散列比使用非自等对象更具病态性。现在我很困惑——我在iPhone上运行的是Code2Go,其中{p}中的
p在2.7.9和3.4.2中是
True
。现在我在约塞米蒂的MacBook上,其中对于2.7.9和3.4.3来说,
p in{p}
False
。@Ethantfurman我在某种程度上同意这一点,但是“
x in y
相当于
any(x是e或x==e in y)
”似乎非常明确——身份首先被检查,所以
p in{p}
应为
True
,但它已实现(因为您不能覆盖标识)。Python是一种非常动态的语言,假设几乎所有关于
x
实现的东西都是愚蠢的,但如果这实际上与底层实现不相等,至少应该对其进行警告。@jornsharpe:--至少会记录解决方案。我不明白你说的是什么意思“这种行为是由NaNs驱动的”。为什么一种导致NaNs出现奇怪行为的行为会是由NaNs驱动的?这听起来好像他们是专门为了打破NaNs而这样做的。
>>> p in [p]
True
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