Proposal: === and !=== operators

Steven D'Aprano steve+comp.lang.python at
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> 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 
anything else.

(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):

class list:
    def __eq__(self, other):
        if self is other:
            return True
        elif isinstance(other, list):
            if len(self) != len(other):
                return False
            for a, b in zip(self, other):
                if not (a is b or a == b):
                    return False
            return True
            return NotImplemented  # Let other decide.


More information about the Python-list mailing list