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/