[Python-Dev] Definition of equality check behavior (original) (raw)

Tim Peters tim.peters at gmail.com
Tue May 7 21:23:35 EDT 2019


[Jordan Adler <jordan.m.adler at gmail.com>]

Through the course of work on the future polyfills that mimic the behavior of Py3 builtins across versions of Python, we've discovered that the equality check behavior of at least some builtin types do not match the documented core data model.

Specifically, a comparison between a primitive (int, str, float were tested) and an object of a different type always return False, instead of raising a NotImplementedError. Consider 1 == '1' as a test case. Should the data model be adjusted to declare that primitive types are expected to fallback to False, or should the cpython primitive type's eq implementation fallback to raise NotImplementedError?

Nope ;-) This isn't a "data model" issue. Look instead at the Standard Library manual's section on Built-In Types, under heading Comparisons:

""" Objects of different types, except different numeric types, never compare equal. ... The <, <=, > and >= operators will raise a TypeError exception when comparing a complex number with another built-in numeric type, when the objects are of different types that cannot be compared, or in other cases where there is no defined ordering. """

It's not an object's responsibility to arrange for that. It's done for them by default, and objects only need to supply their own rich comparison methods if they don't want the defaults. For example, when comparing an int with another type, all the int rich comparison methods do return NotImplemented:

f = 4 f.eq("abc") NotImplemented

It's at a higher level that comparison logic says "OK, I gave both comparands a chance, and they both returned NotImplemented. So one last chance (from object.c's do_richcompare())":

/* If neither object implements it, provide a sensible default
   for == and !=, but raise an exception for ordering. */
switch (op) {
case Py_EQ:
    res = (v == w) ? Py_True : Py_False;
    break;
case Py_NE:
    res = (v != w) ? Py_True : Py_False;
    break;
default:
    PyErr_Format(PyExc_TypeError,
                 "'%s' not supported between instances of '%.100s'

and '%.100s'", opstrings[op], v->ob_type->tp_name, w->ob_type->tp_name); return NULL; }

Then the Py_EQ case of that delivers:

f == "abc" False

and the Py_NE case:

f != "abc" True

despite that (or because of that ;-) ):

f.eq("abc") NotImplemented "abc".eq(f) NotImplemented

Note that there's nothing special about builtin types here. All types are treated alike.



More information about the Python-Dev mailing list