Pretty much anyone who works with IEEE floating-point values has run into NaN, or "not a number", at some point. Famously, NaN is not equal to itself.
>>> x = float('nan')
>>> x == x
False
Now, I had come to terms with this, but there’s a strange behavior I’m struggling to wrap my head around. Namely,
>>> x in [x]
True
I had always assumed that list.__contains__ was written something like
def __contains__(self, element):
for x in self:
if element == x:
return True
return False
i.e., it used __eq__ on the relevant data type internally. And indeed it does. If I define a custom class with an __eq__ method of my own design, then I can verify that Python does in fact call __eq__ when doing the inclusion check. But then how can there exist a value x (NaN in our case) such that x == x is false but x in [x] is true?
We can observe the same behavior with a custom __eq__ as well.
class Example:
def __eq__(self, other):
return False
x = Example()
print(x == x) # False
print(x in [x]) # True
>Solution :
According to the docs it first uses the is operator to check for equality, and since x is x is True, x in [x] is also True:
For container types such as list, tuple, set, frozenset, dict, or collections.deque, the expression
x in yis equivalent toany(x is e or x == e for e in y).
Note that identity (is) is different from equality (==). Also note that not all NaN values are represented by the same object, so if you try your test with two different NaN objects:
>>> float('nan') in [float('nan')]
False
you’ll see different results.