[Python-Dev] Definition of equality check behavior

Jordan Adler jordan.m.adler at gmail.com
Wed May 8 11:51:58 EDT 2019


Ahh, I didn't locate the documentation on the NotImplemented constant
<https://docs.python.org/3/library/constants.html#NotImplemented> as I was
checking 2.7's docs, so I wasn't aware of the layer of indirection and
fallback behavior for the operator.   Sorry about that!

Based on that, the right call here is to adjust newstr to return that
singleton where appropriate.

On Tue, May 7, 2019 at 9:23 PM Tim Peters <tim.peters at gmail.com> wrote:

> [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.
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-dev/attachments/20190508/ac8b1267/attachment-0001.html>


More information about the Python-Dev mailing list