Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is (numpy.nan, 1) == (numpy.nan, 1)?

Tags:

python

While numpy.nan is not equal to numpy.nan, and (float('nan'), 1) is not equal to float('nan', 1),

(numpy.nan, 1) == (numpy.nan, 1)

What could be the reason? Does Python first check to see if the ids are identical? If identity is checked first when comparing items of a tuple, then why isn't it checked when objects are compared directly?

like image 807
satoru Avatar asked Jun 03 '15 08:06

satoru


3 Answers

When you do numpy.nan == numpy.nan it's numpy that is deciding whether the condition is true or not. When you compare tuples python is just checking if the tuples have the same objects which they do. You can make numpy have the decision by turning the tuples into numpy arrays.

np.array((1, numpy.nan)) == np.array((1,numpy.nan))
>>array([ True, False], dtype=bool)

The reason is when you do == with numpy objects you're calling the numpy function __eq__() that says specifically that nan != nan because mathematically speaking nan is undetermined (could be anything) so it makes sense that nan != nan. But when you do == with tuples you call the tuples __eq__() function that doesn't care about mathematics and only cares if python objects are the same or not. In case of (float('nan'),1)==(float('nan'),1) it returns False because each call of float('nan') allocates memory in a different place as you can check by doing float('nan') is float('nan').

like image 106
João Abrantes Avatar answered Nov 19 '22 06:11

João Abrantes


Container objects are free to define what equality means for them, and for most that means one thing is really, really important:

for x in container:
    assert x in container

So containers typically do an id check before an __eq__ check.

like image 22
Ethan Furman Avatar answered Nov 19 '22 07:11

Ethan Furman


When comparing two objects in a tuple Python first check to see if they are the same.

Note that numpy.nan is numpy.nan, but float('nan') is not float('nan').

In Objects/tupleobject.c, the comparison is carried out like this:

for (i = 0; i < vlen && i < wlen; i++) {
    int k = PyObject_RichCompareBool(vt->ob_item[i],
                                     wt->ob_item[i], Py_EQ);
    if (k < 0)
        return NULL;
    if (!k)
        break;
}

And in PyObject_RichCompareBool, you can see the check for equality:

if (v == w) {
    if (op == Py_EQ)
        return 1;
    else if (op == Py_NE)
        return 0;
}

You can verify this with the following example:

class A(object):
    def __eq__(self, other):
        print "Checking equality with __eq__"
        return True

a1 = A()
a2 = A()

If you try (a1, 1) == (a1, 1) nothing get printed, while (a1, 1) == (a2, 1) would use __eq__ and print our the message.

Now try a1 == a1 and see if it surprises you ;P

like image 43
satoru Avatar answered Nov 19 '22 06:11

satoru