
This issue trackerhas been migrated toGitHub, and is currentlyread-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.
Created on2008-11-23 18:21 byterry.reedy, last changed2022-04-11 14:56 byadmin. This issue is nowclosed.
| Files | ||||
|---|---|---|---|---|
| File name | Uploaded | Description | Edit | |
| default-ne-reflected-priority.patch | martin.panter,2015-01-11 06:27 | review | ||
| default-ne-reflected-priority.v2.patch | martin.panter,2015-01-25 22:15 | review | ||
| default-ne-reflected-priority.v3.patch | martin.panter,2015-07-20 04:43 | Resolve conflict | review | |
| default-ne-reflected-priority.v4.patch | martin.panter,2015-07-20 07:27 | review | ||
| Messages (20) | |||
|---|---|---|---|
| msg76270 -(view) | Author: Terry J. Reedy (terry.reedy)*![]() | Date: 2008-11-23 18:21 | |
3.0c3 doc (Basic customization) says"There are no implied relationships among the comparison operators. Thetruth of x==y does not imply that x!=y is false. Accordingly, whendefining __eq__(), one should also define __ne__() so that the operatorswill behave as expected. "Inhttp://mail.python.org/pipermail/python-ideas/2008-October/002235.htmlGuido says"I should also note that part of George's proposal has already beenimplemented: if you define __eq__, you get a complementary __ne__ forfree. However it doesn't work the other way around (defining __ne__doesn't give you __eq__ for free), and there is no similarrelationship for the ordering operators."And indeed, as Arnaud Delobelle posted on python-listclass A: def __init__(self, x): self.x = x def __eq__(self, other): return self.x == other.xa, b, c = A(1), A(1), A(2)print(a==b, b==c, c==a) # True, False, Falseprint(a!=b, b!=c, c!=a) # False, True, TrueSuggested revision:"There is one implied relationship among comparison operators: defining__eq__ gives an automatic __ne__ (but not the other way). There is nosimilar relationship for the order comparisons. | |||
| msg76374 -(view) | Author: Michael K. Edwards (medwards) | Date: 2008-11-25 00:49 | |
It would be really useful to explain, right in this section, why __ne__is worth having. Something along these lines (based on the logic fromPython 2.x -- modify as necessary):<doctext>The values most commonly returned by the rich comparison methods areTrue, False, and NotImplemented (which tells the Python interpreter totry a different comparison strategy). However, it is quite legal andoften useful to return some other value, usually one which can becoerced to True/False by bool().For instance, if equality testing of instances of some class iscomputationally expensive, that class's implementation of __eq__ mayreturn a "comparison object" whose __nonzero__ method calculates andcaches the actual boolean value. Subsequent references to this samecomparison object may be returned for subsequent, logically equivalentcomparisons; the expensive comparison takes place only once, when theobject is first used in a boolean context. This class's implementationof __ne__ could return, not just "not (self == other)", but an objectwhose __nonzero__ method returns "not (self == other)" -- potentiallydelaying the expensive operation until its result is really tested forboolean truth.Python allows the programmer to define __ne__ separately from __eq__ forthis and similar reasons. It is up to the programmer to ensure thatbool(self != other) == (not bool(self == other)), if this is a desiredproperty. (One can even imagine situations in which it is appropriatefor neither (self == other) nor (self != other) to be true. Forinstance, a mathematical theorem prover might contain values a, b, c,... that are formally unknown, and raise an exception when a==b is usedin a boolean context, but allow comparison of M = (a==b) against N =(a!=b).)</doctext>Now that I write this, I see a use for magic __logical_or__,__logical_and__, and __logical_not__ methods, so that one can postponeor even avoid the evaluation of expensive/indeterminate comparisons. Consider the expression: ((a==b) and (c==d)) and ((a!=b) and (d==f))If my class is designed such that a==b and a!=b cannot both be true,then I can conclude that this expression is false without evaluating anyof the equality/inequality tests.Is it too late to request these for Python 3.0? | |||
| msg89532 -(view) | Author: Terry J. Reedy (terry.reedy)*![]() | Date: 2009-06-19 23:06 | |
The current paragraph"There are no implied relationships among the comparison operators. Thetruth of x==y does not imply that x!=y is false. Accordingly, whendefining __eq__(), one should also define __ne__() so that the operatorswill behave as expected. "is false.Please, let us replace it now, for 3.1 release, with the correct"There is one implied relationship among comparison operators: defining__eq__ gives an automatic __ne__ (but not the other way). There is nosimilar relationship for the order comparisons."without waiting for a more extensive rewrite. | |||
| msg89533 -(view) | Author: Raymond Hettinger (rhettinger)*![]() | Date: 2009-06-19 23:17 | |
One other thought: The __ne__ method follows automatically from __eq__only if __ne__ isn't already defined in a superclass. So, if you'reinheriting from a builtin, it's best to override both. | |||
| msg89553 -(view) | Author: Terry J. Reedy (terry.reedy)*![]() | Date: 2009-06-21 01:29 | |
The situation appears to be at least slightly different from what Guidostated. In 3.x, all classes subclass object, which has .__ne__, so ifthat stopped inferred != behavior, it would never happen.>>> class A:def __eq__(s,p): return 1>>> id(object.__ne__)10703216>>> id(A.__ne__)10703216No new A.__ne__ added. But>>> c,d=object(),object()>>> c==dFalse>>> c!=dTrue>>> a,b = A(),A()>>> a==b1>>> a!=bFalseSo it seems that a!=b *is* evaluated as not a==b rather than asa.__ne__(b). If so, my revised suggested replacement would be:"There is one implied relationship among comparison operators: defining__eq__ causes '!=' to be evaluated as 'not ==' (but not the other way). There is no similar relationship for the order comparisons."I am a bit puzzled though. Inttp://svn.python.org/view/python/branches/py3k/Python/ceval.c?revision=73066&view=markupI traced compare_op to cmp_outcome to (in object.c) PyOjbect_RichCompareto do_richcompare to class specific tp_richcompare and I do not see thespecial casing of eq. However, I am newbie at codebase. | |||
| msg89591 -(view) | Author: Michael K. Edwards (medwards) | Date: 2009-06-22 03:47 | |
The implementation you are looking for is in object_richcompare, inhttp://svn.python.org/projects/python/branches/py3k/Objects/typeobject.c. It would be most accurate to say something like: The "object" base class, from which all user-defined classesinherit, provides a single "rich comparison" method to which all of thecomparison operators (__eq__, __ne__, __lt__, __le__, __ge__, __gt__)map. This method returns a non-trivial value (i. e., something otherthan NotImplemented) in only two cases: * When called as __eq__, if the two objects are identical, this methodreturns True. (If they are not identical, it returns NotImplemented sothat the other object's implementation of __eq__ gets a chance to returnTrue.) * When called as __ne__, it calls the equivalent of "self == other";if this returns a non-trivial value X, then it returns !X (which isalways either True or False). | |||
| msg89592 -(view) | Author: Michael K. Edwards (medwards) | Date: 2009-06-22 05:08 | |
It would also be useful to point out that there is a shortcut in theinterpreter itself (PyObject_RichCompareBool, in object.c) which checksthe equivalent of id(a) == id(b) and bypasses __eq__/__ne__ if so. Since not every call to __eq__ passes through this function, it's fairlyimportant that implementations of __eq__ return either True orNotImplemented when id(a) == id(b). Ditto for extension modules;anything that installs its own tp_richcompare should handle objectidentity and __ne__ in substantially the same way, so that subclassauthors can rely on the documented behavior when overriding __eq__. | |||
| msg181630 -(view) | Author: Mark Dickinson (mark.dickinson)*![]() | Date: 2013-02-07 16:28 | |
Issue#17151 closed as a duplicate of this one. | |||
| msg233835 -(view) | Author: Martin Panter (martin.panter)*![]() | Date: 2015-01-11 06:27 | |
Here is a patch that documents the default object.__ne__() implementation. It also documents the subclass priority rules for the reflected comparison methods, which is raised inIssue 22052.I have made some more tests to verify the relationships exists from __ne__ to __eq__, but no other relationships exist for the other methods. I will include it in my patch inIssue 21408 to avoid the patches conflicting with each other. | |||
| msg234601 -(view) | Author: Alyssa Coghlan (ncoghlan)*![]() | Date: 2015-01-24 11:02 | |
While Martin's patch doesn't cover all the vagaries of comparison operations discussed above, it fixes the outright error, and provides an appropriate cross-reference to functools.total_ordering. | |||
| msg234605 -(view) | Author: Martin Panter (martin.panter)*![]() | Date: 2015-01-24 12:40 | |
The reference to @functools.total_ordering was actually already there; I just moved it into the paragraph about relationships between the operators. I should also point out that my description of the default __ne__() assumes thatIssue 21408 is resolved; the current behaviour is slightly different.If you think something else could be added to the patch, I’m happy to try and add it. Perhaps the default object.__eq__() behaviour? | |||
| msg234696 -(view) | Author: Martin Panter (martin.panter)*![]() | Date: 2015-01-25 22:15 | |
Adding a new patch that just fixes the typo error in the first patch | |||
| msg234780 -(view) | Author: Serhiy Storchaka (serhiy.storchaka)*![]() | Date: 2015-01-26 21:35 | |
See alsoissue23326. | |||
| msg236012 -(view) | Author: Martin Panter (martin.panter)*![]() | Date: 2015-02-15 00:12 | |
Issue 21408 has been committed to 3.4 and 3.5 branches, so my patch can now be considered to document the newly fixed behaviour. | |||
| msg246955 -(view) | Author: Martin Panter (martin.panter)*![]() | Date: 2015-07-20 04:43 | |
Nick seemed to approve of this, so perhaps it is ready to commit? The new patch just resolves a minor conflict with the current code. | |||
| msg246959 -(view) | Author: Serhiy Storchaka (serhiy.storchaka)*![]() | Date: 2015-07-20 05:22 | |
Added comments on Rietveld. | |||
| msg246968 -(view) | Author: Martin Panter (martin.panter)*![]() | Date: 2015-07-20 07:27 | |
This updated patch adds the clarification about NotImplemented. | |||
| msg246969 -(view) | Author: Serhiy Storchaka (serhiy.storchaka)*![]() | Date: 2015-07-20 08:12 | |
LGTM. | |||
| msg248155 -(view) | Author: Roundup Robot (python-dev)![]() | Date: 2015-08-06 22:34 | |
New changesetf5069e6e4229 by Robert Collins in branch '3.4':Issue#4395: Better testing and documentation of binary operators.https://hg.python.org/cpython/rev/f5069e6e4229New changesetb9a0165a3de8 by Robert Collins in branch '3.5':Issue#4395: Better testing and documentation of binary operators.https://hg.python.org/cpython/rev/b9a0165a3de8New changesete56893df8e76 by Robert Collins in branch 'default':Issue#4395: Better testing and documentation of binary operators.https://hg.python.org/cpython/rev/e56893df8e76 | |||
| msg248156 -(view) | Author: Robert Collins (rbcollins)*![]() | Date: 2015-08-06 22:43 | |
Thanks for the patch; applied to 3.4 and up. | |||
| History | |||
|---|---|---|---|
| Date | User | Action | Args |
| 2022-04-11 14:56:41 | admin | set | github: 48645 |
| 2015-08-06 22:43:02 | rbcollins | set | status: open -> closed nosy: +rbcollins messages: +msg248156 resolution: fixed stage: commit review -> resolved |
| 2015-08-06 22:34:53 | python-dev | set | nosy: +python-dev messages: +msg248155 |
| 2015-07-20 08:12:44 | serhiy.storchaka | set | messages: +msg246969 |
| 2015-07-20 07:27:29 | martin.panter | set | files: +default-ne-reflected-priority.v4.patch messages: +msg246968 |
| 2015-07-20 05:22:58 | serhiy.storchaka | set | messages: +msg246959 |
| 2015-07-20 04:43:18 | martin.panter | set | files: +default-ne-reflected-priority.v3.patch stage: patch review -> commit review messages: +msg246955 versions: + Python 3.6 |
| 2015-03-19 11:24:37 | Arfrever | set | nosy: +Arfrever |
| 2015-02-15 00:12:09 | martin.panter | set | messages: +msg236012 |
| 2015-01-26 21:35:37 | serhiy.storchaka | set | nosy: +serhiy.storchaka messages: +msg234780 |
| 2015-01-25 22:15:47 | martin.panter | set | files: +default-ne-reflected-priority.v2.patch messages: +msg234696 |
| 2015-01-24 12:40:46 | martin.panter | set | messages: +msg234605 |
| 2015-01-24 11:02:00 | ncoghlan | set | messages: +msg234601 |
| 2015-01-11 16:17:03 | berker.peksag | set | nosy: +berker.peksag |
| 2015-01-11 06:39:54 | asvetlov | set | stage: needs patch -> patch review |
| 2015-01-11 06:39:37 | asvetlov | set | versions: + Python 3.4, Python 3.5, - Python 3.2, Python 3.3 |
| 2015-01-11 06:27:32 | martin.panter | set | files: +default-ne-reflected-priority.patch nosy: +martin.panter messages: +msg233835 keywords: +patch |
| 2013-02-07 16:28:53 | mark.dickinson | set | priority: low -> normal |
| 2013-02-07 16:28:32 | mark.dickinson | set | nosy: +mark.dickinson,franck messages: +msg181630 |
| 2013-02-07 16:27:09 | mark.dickinson | link | issue17151 superseder |
| 2012-12-08 11:01:54 | chris.jerdonek | set | nosy: +chris.jerdonek |
| 2012-12-08 10:39:52 | ncoghlan | set | nosy: +antocuni |
| 2012-12-08 10:39:08 | ncoghlan | set | nosy: +ncoghlan |
| 2011-11-18 14:32:50 | eric.araujo | set | nosy: +eric.araujo |
| 2011-11-15 20:07:02 | ezio.melotti | set | versions: + Python 3.3, - Python 3.1 |
| 2011-01-12 00:21:00 | eric.araujo | set | nosy: +docs@python, -georg.brandl stage: needs patch type: behavior versions: + Python 3.2 |
| 2010-09-02 00:45:05 | rhettinger | set | priority: normal -> low assignee:rhettinger ->terry.reedy |
| 2009-08-16 11:53:25 | cvrebert | set | nosy: +cvrebert |
| 2009-06-22 05:08:35 | medwards | set | messages: +msg89592 |
| 2009-06-22 03:48:00 | medwards | set | messages: +msg89591 |
| 2009-06-21 04:36:52 | rhettinger | set | assignee:georg.brandl ->rhettinger |
| 2009-06-21 01:29:51 | terry.reedy | set | messages: +msg89553 |
| 2009-06-19 23:17:50 | rhettinger | set | nosy: +rhettinger messages: +msg89533 |
| 2009-06-19 23:06:12 | terry.reedy | set | messages: +msg89532 versions: + Python 3.1, - Python 3.0 |
| 2008-11-25 00:49:33 | medwards | set | nosy: +medwards messages: +msg76374 title: Document auto __ne__ generation -> Document auto __ne__ generation; provide a use case for non-trivial __ne__ |
| 2008-11-23 18:21:33 | terry.reedy | create | |