[Python-Dev] Definition of equality check behavior

2019-05-07 Thread Jordan Adler
Hey folks!

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?

Reasonable people could disagree about the right approach, but my distaste
for silent failures leads me to recommend that the implementation be
adjusted to return NotImplementedError as a fallback, and to document that
the operands should not be coerced to the same type prior to comparison
(enforcing a stricter equality check). This will of course require a
deprecation protocol.

Alternatively some new equality operator could be used to specify the level
of coercion/type checking desired (currently Python has 'is' and '==').

Jordan
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] Definition of equality check behavior

2019-05-08 Thread Jordan Adler
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  wrote:

> [Jordan Adler ]
> > 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.
>
___
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com