Proposal: === and !=== operators
steve+comp.lang.python at pearwood.info
Sat Jul 12 10:33:03 CEST 2014
On Sat, 12 Jul 2014 01:07:28 -0400, Alan Bawden wrote:
> Steven D'Aprano <steve+comp.lang.python at pearwood.info> writes:
>> But perhaps we only care about changes in value, not type. NAN or no
>> NAN, list equality works fine:
>> py> data = [1.0, 2.0, float('nan'), 4.0]
>> py> old = data[:]
>> py> old == data # No changes made yet, should return True True
> You lost me right here. If list equality is determined by comparing
> lists element-by-element, and the second element of old is _not_ equal
> to the second element of data, then why should old and data be equal?
Because practicality beats purity.
Outside of the specialist areas of IEEE-754 floating point NANs, and SQL
NUL, it is a basic property of *nearly everything* that x equals itself,
for any x. This property is called "reflexivity", and is considered by
some (although not me) to be absolutely fundamental, never to be violated
under any circumstances.
(To be clear, I'm talking about reflexivity of *equality* specifically,
not of every operator. We wouldn't expect x > x, for example -- the
"greater than" operator is not reflexive.)
While IEEE-754, and SQL, have good reasons for violating the reflexivity
of equals, that upsets a lot of people. Given:
data = [1.0, 2.0, 3.0]
data == data
it would be surprising, and annoying, more often than useful if the
equality test returned False. Replace any of the floats with a NAN, and
the same surprise and annoyance applies.
In the case of Python, lists and other builtin containers partially
violate the specialist rules of IEEE-754 NAN handling, by using "is"
identity test as a shortcut for equality. Effectively, they assume that
equality is always reflexive. This was introduced as an optimization,
since == can be quite expensive in Python, whereas "is" requires only a
fast pointer comparison and is very cheap.
I support this optimization, even if it violates the non-reflexivity of
NANs. NANs are specialised values, and outside of the narrow confines of
IEEE-754 arithmetic, their non-reflexivity is more of a nuisance than
(Some would argue that *even within* IEEE-754 arithmetic, non-reflexivity
is a nuisance. For now, I prefer to remain neutral in that argument.)
> In fact, I find myself puzzled about exactly how list equality is
> actually defined.
You'd have to check the C source code, but I would expect something like
this (only written in C):
def __eq__(self, other):
if self is other:
elif isinstance(other, list):
if len(self) != len(other):
for a, b in zip(self, other):
if not (a is b or a == b):
return NotImplemented # Let other decide.
More information about the Python-list