Proposal: === and !=== operators
Steven D'Aprano
steve at pearwood.info
Wed Jul 9 05:02:55 EDT 2014
On Wed, 09 Jul 2014 18:17:23 +1000, Cameron Simpson wrote:
> On 09Jul2014 07:00, Steven D'Aprano <steve at pearwood.info> wrote:
>>At the moment, Python has two (in)equality operators, == and != which
>>call __eq__ and __ne__ methods. Some problems with those:
>>
>>* Many people expect == to always be reflexive (that is, x == x for
>> every x) but classes which customise __eq__ may not be.
>
> I'm presuming this proposal is fallout from the Nan anecdotes, since NaN
> != Nan?
There is *yet another* bloody argument going on about NANs on Python-Dev,
from people who (for the most part) don't do numeric programming but feel
sure that they know better than William Kahan and the IEEE-754 committee
that designed it :-(
> The language spec is at least up front about it, I thought. It could be
> plainer, but at least it says:
>
> Furthermore, some types (for example, function objects) support only
> a degenerate notion of comparison where any two objects of that type
> are unequal.
>
> which implies nonreflexivity.
Functions inherit the default behaviour of __eq__ from object, which
falls back on identity:
py> def f(): return 1
...
py> f == f
True
> Returning to Nan, I had thought it was an explicit design choice in IEEE
> floating point that NaN != NaN so that in (hypothetically) common cases
> results won't accidentally issue truthiness in the vein of propagating
> evaluation errors.
There are various reasons for why the NANs always compare unordered
(including unequal), but yes it is a deliberate decision. Unfortunately
people who aren't doing numeric work (and a few who are) don't like it,
and don't like that it breaks certain "common sense"
>
> Personally I'd go for Nan == Nan raising a ValueError myself, but that
> is a bikeshed I lack the expertise to paint.
If we can do other nonsensical comparisons and get False, why treat NANs
differently?
py> "Hello World" == {2.5: None}
False
For what it's worth, the IEEE-754 standard supports the "exception on
comparison" model with signalling NANs, unfortunately C99 does not
support signalling NANs and Java explicitly forbids them.
> Anyway, I thought it is a design feature that a class can arrange for
> nonreflexivity in ==. Surprising, maybe, but wouldn't use of such a
> special class be known to the user?
Well yes, but then you have folks like Anders ("NaN comparisons - Call
For Anecdotes" thread) who didn't know that floats are not reflexive (as
well as not transitive, associative, or commutative). He's hardly the
only one -- Stackoverflow appears to get a question asking about NANs
about three times a week. More broadly, if you're writing a generic
library which will be used with arbitrary objects, there are very few
assumptions you can make about them -- nevertheless people do.
> Have we got some examples of people using nonreflexive == classes and
> being burnt? Aside from Nan, which I'd argue is a well known special
> case, or should be.
Yes it should be, and no I don't, but people are *extremely* vehement
that x == x ought to return True for any x. In my opinion, reflexivity is
not that important outside of pure mathematics and logic, and people only
get upset about the lack of it because it goes against their intuition
about what it means for two things to be equal. But other clever people
disagree, and even though they're wrong *wink* I'd rather seek a
compromise that gives everybody what they want.
>>* The == operator requires __eq__ to return True or False
>> (or NotImplemented) and raises TypeError if it doesn't, which makes it
>> impossible to use == with (say) three-valued or fuzzy logic.
Hmmm... I could have sworn that == raised an exception if __eq__ returned
something other than True/False/NotImplemented, but apparently I was
wrong. Maybe I dreamt it.
> I don't see this type constraint you describe.
Neither do I.
>>I propose:
>>
>>* The == operator be redefined to *always* assume reflexivity, that
>> is, it first compares the two arguments using `is` before calling the
>> __eq__ methods.
>
> Won't this slow down every == test?
Only by a pointer comparison, which is very fast. Compared to the cost of
looking up __eq__ and calling it, the extra cost will be insignificant,
and since many objects (small ints, certain strings, etc.) are cached,
the over-all result will probably be to speed up the average == test.
>>* That's a backwards-incompatible change, so you need to enable it
>> using "from __future__ import equals" in Python 3.5, and then to
>> become the default behaviour in 3.6.
>>
>>* To support non-reflexive types, allow === and !=== operators, which
>> are like == and != except they don't call `is` first.
> [...]
>
> I don't like the spelling. They seem very easy to misuse as typos of
> conventional == and !=, and not visually very different.
If you can tell the difference between x=y and x==y you should be able to
also distinguish x===y. But I accept that it's a little sub-optimal.
--
Steven
More information about the Python-list
mailing list