1

There is a magic method called __ne__ in Python which is triggered on objects != comparison.

Example:

class A(object):
    def __init__(self, a):
        self.a = a

    def __ne__(self, other):
        return self.a != other.a

A(3) != A(3) # produces False
A(3) != A(2) # produces True

The Question:

What happens under the hood if __ne__ is not defined?

Note: In python 3.x != comparison is defined to be invert of whatever __eq__ returns.

I thought that object ids are compared, in this case, assuming that we do not have singleton, all != comparisons would have to return True. But apparently the same code on different environments was producing different results, so I guess, there is something else which is compared instead of object ids.

vovaminiof
  • 511
  • 1
  • 4
  • 13

2 Answers2

1

If you wouldn't use explicit __ne__ in your class definition, then __ne__ from inherited object will be used. It works like following code (but of course original is written in C):

def __ne__(self, other):
    eq_result = self == other
    if eq_result is NotImplemented:
        return NotImplemented
    else:
        return not eq_result

So because you're comparing user-defined classes, then id is being used.

Here is the source code source code. Look at slot_tp_richcompare.

turkus
  • 4,637
  • 2
  • 24
  • 28
  • Where did you take it from? Do you have a link to C implementation of this functionality ? – vovaminiof Sep 29 '16 at 15:42
  • The python code here is highly misleading. Python 2.7 (as opposed to 3.x) does not use `__eq__` (as implied by this code) if `__ne__` is not defined. Instead it used the `id` (as you say), even id `__eq__` is defined independent of the id. – Bernhard Nov 19 '18 at 13:30
0

turkus' answer is correct: if no __ne__() method is specified, the inverse of the result of the __eq__() method is returned. The relevant part of the CPython source is slot_tp_richcompare():

static PyObject *
slot_tp_richcompare(PyObject *self, PyObject *other, int op)
{
    PyObject *res;

    if (Py_TYPE(self)->tp_richcompare == slot_tp_richcompare) {
        res = half_richcompare(self, other, op);
        if (res != Py_NotImplemented)
            return res;
        Py_DECREF(res);
    }
    if (Py_TYPE(other)->tp_richcompare == slot_tp_richcompare) {
        res = half_richcompare(other, self, _Py_SwappedOp[op]);
        if (res != Py_NotImplemented) {
            return res;
        }
        Py_DECREF(res);
    }
    Py_INCREF(Py_NotImplemented);
    return Py_NotImplemented;
}

If __ne__() is not defined, the opposite method (defined in _Py_SwappedOp[op] as Py_EQ, i.e. __eq__(), for Py_NE) is called. A comment in classobject.c shows what happens should __eq__() not be defined:

/* If there is no __eq__ and no __cmp__ method, we hash on the address. If an __eq__ or __cmp__ method exists, there must be a __hash__. */

kfb
  • 6,252
  • 6
  • 40
  • 51
  • If I'm not mistaken, the opposite of `Py_EQ` defined in `_Py_SwappedOp[op]` is `Py_EQ`. Swapping only means swapping left and right side, so `a!=b` is swapped to `b!=a`. (Note that `aa`. So even if `__eq__` is defined, python 2.7 (as opposed to 3.x) des not use it for `__ne__`. See https://stackoverflow.com/questions/4352244/python-should-i-implement-ne-operator-based-on-eq – Bernhard Nov 19 '18 at 13:29