On Wed, 09 Jul 2014 18:17:23 +1000, Cameron Simpson wrote: > On 09Jul2014 07:00, Steven D'Aprano <st...@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 -- https://mail.python.org/mailman/listinfo/python-list