You're right that it's not only about list.count().

IMO it's a good optimization to skip __eq__() when id(x) == id(y). But
it can be surprising, so I just would like to document it somewhere.
For example, in __eq__() method documentation:
https://docs.python.org/dev/reference/datamodel.html#object.__eq

list.count(), list.index(), tuple.count() and tuple.index() all
consider that two elements are equal if id(x) == id(y):

>>> nan=float("nan")
>>> l=[nan]*5
>>> l.count(nan)
5
>>> l.count(nan)
5
>>> l.index(nan)
0
>>> t=(nan,)*5
>>> t.count(nan)
5
>>> t.index(nan)
0

A second NaN object is equal if you consider they bytes content in
memory, but float.__eq__() returns False, and list.count() and
list.index() see them as different (which is the expected behavior):

>>> import struct
>>> nan2=float("nan")
>>> struct.pack("<d", nan) == struct.pack("<d", nan2)
True
>>> nan2 == nan  # float.__eq__(nan2, nan)
False
>>> nan2 is nan
False
>>> l.count(nan2)
0
>>> l.index(nan2)
ValueError: nan is not in list

Python dict also skips __eq__() if id(x) == id(y):

Python 3.9.0a2+ (heads/requires_setenv-dirty:f5cfdf2431, Jan 23 2020, 04:15:01)
>>> nan=float("nan")
>>> d={nan: "not a number"}
>>> d[nan]
'not a number'

Another dict example:

>>> class NotEq:
...   def __eq__(self, other): return False
...   def __hash__(self): return 3
...
>>> key=NotEq()
>>> d2={key: 'not equal'}
>>> d2[key]
'not equal'

Python set also skips __eq_():

>>> s={key}
>>> key in s
True

Victor

Le ven. 24 janv. 2020 à 02:37, Tim Peters <tim.pet...@gmail.com> a écrit :
>
> PyObject_RichCompareBool(x, y, op) has a (valuable!) shortcut:  if x
> and y are the same object, then equality comparison returns True and
> inequality False.  No attempt is made to execute __eq__ or __ne__
> methods in those cases.
>
> This has visible consequences all over the place, but they don't
> appear to be documented.  For example,
>
> >>> import math
> >>> ([math.nan] * 5).count(math.nan)
> 5
>
> despite that `math.nan == math.nan` is False.
>
> It's usually clear which methods will be called, and when, but not
> really here.  Any _context_ that calls PyObject_RichCompareBool()
> under the covers, for an equality or inequality test, may or may not
> invoke __eq__ or __ne__, depending on whether the comparands are the
> same object.  Also any context that inlines these special cases to
> avoid the overhead of calling PyObject_RichCompareBool() at all.
>
> If it's intended that Python-the-language requires this, that needs to
> be documented.
>
> Or if it's implementation-defined, then _that_ needs to be documented.
>
> Which isn't straightforward in either case, in part because
> PyObject_RichCompareBool isn't a language-level concept.
>
> This came up recently when someone _noticed_ the list.count(NaN)
> behavior, and Victor made a PR to document it:
>
> https://github.com/python/cpython/pull/18130
>
> I'm pushing back, because documenting it _only_ for .count() makes
> .count() seem unique in a way it isn't, and doesn't resolve the
> fundamental issue:  is this language behavior, or implementation
> behavior?
>
> Which I don't want to argue about.  But you certainly should ;-)
> _______________________________________________
> Python-Dev mailing list -- python-dev@python.org
> To unsubscribe send an email to python-dev-le...@python.org
> https://mail.python.org/mailman3/lists/python-dev.python.org/
> Message archived at 
> https://mail.python.org/archives/list/python-dev@python.org/message/3ZAMS473HGHSI64XB3UV4XBICTG2DKVF/
> Code of Conduct: http://python.org/psf/codeofconduct/



-- 
Night gathers, and now my watch begins. It shall not end until my death.
_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/python-dev@python.org/message/N45H65U7OJQH2ESX3CI4AXZI6PBI5TNB/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to