[Python-Dev] == on object tests identity in 3.x - list delegation to members?

Steven D'Aprano steve at pearwood.info
Sun Jul 13 18:23:03 CEST 2014

On Sun, Jul 13, 2014 at 05:13:20PM +0200, Andreas Maier wrote:

> Second, if not by delegation to equality of its elements, how would the 
> equality of sequences defined otherwise?

Wow. I'm impressed by the amount of detailed effort you've put into 
investigating this. (Too much detail to absorb, I'm afraid.) But perhaps 
you might have just asked on the python-list at python.org mailing list, or 
here, where we would have told you the answer:

    list __eq__ first checks element identity before going on
    to check element equality.

If you can read C, you might like to check the list source code:


but if I'm reading it correctly, list.__eq__ conceptually looks 
something like this:

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

(The actual code is a bit more complex than that, since there is a 
single function, list_richcompare, which handles all the rich 

The critical test is PyObject_RichCompareBool here:


which explicitly says:

    /* Quick result when objects are the same.
       Guarantees that identity implies equality. */

> I added this test only to show that float NaN is a special case,

NANs are not a special case. List __eq__ treats all object types 
identically (pun intended):

py> class X:
...     def __eq__(self, other): return False
py> x = X()
py> x == x
py> [x] == [X()]
py> [x] == [x]

> Case 6.c) is the surprising case. It could be interpreted in two ways 
> (at least that's what I found):
> 1) The comparison is based on identity of the float objects. But that is 
> inconsistent with test #4. And why would the list special-case NaN 
> comparison in such a way that it ends up being inconsistent with the 
> special definition of NaN (outside of the list)?

It doesn't. NANs are not special cased in any way.

This was discussed to death some time ago, both on python-dev and 
python-ideas. If you're interested, you can start here:


which is in the middle of one of the threads, but at least it gets you 
to the right time period.

> 2) The list does not always delegate to element equality, but attempts 
> to optimize if the objects are the same (same identity).

Right! It's not just lists -- I believe that tuples, dicts and sets 
behave the same way.

> We will see 
> later that that happens. Further, when comparing float NaNs of the same 
> identity, the list implementation forgot to special-case NaNs. Which 
> would be a bug, IMHO.

"Forgot"? I don't think the behaviour of list comparisons is an 

NAN equality is non-reflexive. Very few other things are the same. It 
would be seriously weird if alist == alist could return False. You'll 
note that the IEEE-754 standard has nothing to say about the behaviour 
of Python lists containing NANs, so we're free to pick whatever 
behaviour makes the most sense for Python, and that is to minimise the 
"Gotcha!" factor.

NANs are a gotcha to anyone who doesn't know IEEE-754, and possibly even 
some who do. I will go to the barricades to fight to keep the 
non-reflexivity of NANs *in isolation*, but I believe that Python has 
made the right decision to treat lists containing NANs the same as 
everything else.

NAN == NAN  # obeys IEEE-754 semantics and returns False

[NAN] == [NAN]  # obeys standard expectation that equality is reflexive

This behaviour is not a bug, it is a feature. As far as I am concerned, 
this only needs documenting. If anyone needs list equality to honour the 
special behaviour of NANs, write a subclass or an equal() function.


More information about the Python-Dev mailing list