[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:
http://hg.python.org/cpython/file/22e5a85ba840/Objects/listobject.c
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
comparisons.)
The critical test is PyObject_RichCompareBool here:
http://hg.python.org/cpython/file/22e5a85ba840/Objects/object.c
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
False
py> [x] == [X()]
False
py> [x] == [x]
True
[...]
> 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:
https://mail.python.org/pipermail/python-list/2012-October/633992.html
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
accident.
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.
--
Steven
More information about the Python-Dev
mailing list