Define __eq__ for Python C extension type

c, python

Solution

When working with new types defined in C, you need to define tp_richcompare. Below is an implementation of rich compare for a type that always compares larger than all other types (except itself):

static PyObject *
Largest_richcompare(PyObject *self, PyObject *other, int op)
{
    PyObject *result = NULL;

    if (UndefinedObject_Check(other)) {
        result = Py_NotImplemented;
    }
    else {
        switch (op) {
        case Py_LT:
            result = Py_False;
            break;
        case Py_LE:
            result = (LargestObject_Check(other)) ? Py_True : Py_False;
            break;
        case Py_EQ:
            result = (LargestObject_Check(other)) ? Py_True : Py_False;
            break;
        case Py_NE:
            result = (LargestObject_Check(other)) ? Py_False : Py_True;
            break;
        case Py_GT:
            result = (LargestObject_Check(other)) ? Py_False : Py_True;
            break;
        case Py_GE:
            result = Py_True;
            break;
        }
    }

    Py_XINCREF(result);
    return result;
}

If you are using Python 3.x, you add it to the type object like this:

(richcmpfunc)&Largest_richcompare,       /* tp_richcompare */

If you are using Python 2.x, there is an extra step involved. Rich comparisons were added during the lifetime of Python 2.x and for a few versions of Python, a C extension could optionally define tp_richcomare. To inform Python 2.x that your type implements rich comparisons, you need to modify tp_flags by or-ing in Py_TPFLAGS_HAVE_RICHCOMPARE.

Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_RICH_COMPARE,        /* tp_flags */

Problem

I'm having trouble trying to implement `__eq__` for a Rect class I wrote as a C extension. I tried defining a method called `__eq__`, but Python seems to override it. ``` static PyObject * Rect___eq__(Rect *self, PyObject *other) { Rect *rect = (Rect *) other; if (self->x != rect->x || self->y != rect->y || self->width != rect->width || self->height != rect->height) { Py_RETURN_FALSE; } else { Py_RETURN_TRUE; } } static PyMethodDef Rect_methods[] = { {"__eq__", (PyCFunction)Rect___eq__, METH_VARARGS, "Compare Rects" }, {NULL} /* Sentinel */ }; ``` It seems no matter what I do, Python defaults to "is" behavior: ``` >>> a = Rect(1, 2, 3, 4) >>> b = Rect(1, 2, 3, 4) >>> a == b False >>> a == a True ```

Original source