Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
135 views
in Technique[技术] by (71.8m points)

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

If we make a pathological potato like this:

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

We can break sets and dicts this way (note: it's the same even if __eq__ returns True, it's mucking with the hash that broke them):

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

Also len({p: 0, p: 0}) == 2, and {p: 0}[p] raises KeyError, basically all mapping related stuff goes out the window, as expected.

But what I didn't expect is that we can't break lists

>>> p in [p]
True

Why is that? It seems that list.__contains__ iterates, but it's first checking identity before checking equality. Since it is not the case that identity implies equality (see for example NaN object), what is the reason for lists short-circuiting on identity comparisons?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

list, tuple, etc., does indeed do an identity check before an equality check, and this behavior is motivated by 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

Unfortunately, dicts, sets, and friends operate by hashes, so if you mess with those you can indeed effectively break them.

See this issue and this issue for some history.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...