Issue39862

This issue tracker **has been migrated to GitHub**,
and is currently **read-only**.

For more information,
see the GitHub FAQs in the Python's Developer Guide.

Created on **2020-03-05 10:29** by **maggyero**, last changed **2022-04-11 14:59** by **admin**. This issue is now **closed**.

Messages (8) | |||
---|---|---|---|

msg363426 - (view) | Author: Géry (maggyero) * | Date: 2020-03-05 10:29 | |

Mathematically, the [binary relation](https://en.wikipedia.org/wiki/Binary_relation) ≤ is the [union](https://en.wikipedia.org/wiki/Binary_relation#Union) of the binary relations < and =, while the binary relation ≥ is the union of the binary relations > and =. So is there a reason why Python does not implement `__le__` in terms of `__lt__` and `__eq__` by default, and `__ge__` in terms of `__gt__` and `__eq__` by default? The default implementation would be like this (but probably in C for performance, like `__ne__`): ```python def __le__(self, other): result_1 = self.__lt__(other) result_2 = self.__eq__(other) if result_1 is not NotImplemented and result_2 is not NotImplemented: return result_1 or result_2 return NotImplemented def __ge__(self, other): result_1 = self.__gt__(other) result_2 = self.__eq__(other) if result_1 is not NotImplemented and result_2 is not NotImplemented: return result_1 or result_2 return NotImplemented ``` This would save users from implementing these two methods all the time. Here is the relevant paragraph in the [Python documentation](https://docs.python.org/3/reference/datamodel.html#object.__lt__) (emphasis mine): > By default, `__ne__()` delegates to `__eq__()` and inverts the result > unless it is `NotImplemented`. There are no other implied > relationships among the comparison operators, **for example, the truth > of `(x<y or x==y)` does not imply `x<=y`.** *Note.* — These union relationships are always valid, contrary to the following relationships which are only valid for [total orders](https://en.wikipedia.org/wiki/Binary_relation#Properties) (also called connex orders) and therefore not implemented by default: < is the [complement](https://en.wikipedia.org/wiki/Binary_relation#Complement) of ≥, and > is the complement of ≤. These complementary relationships can be easily implemented by users when they are valid with the [`functools.total_ordering`](https://docs.python.org/3/library/functools.html#functools.total_ordering) class decorator provided by the Python standard library. |
|||

msg363442 - (view) | Author: Raymond Hettinger (rhettinger) * | Date: 2020-03-05 16:49 | |

See https://www.python.org/dev/peps/pep-0207/ |
|||

msg363665 - (view) | Author: Géry (maggyero) * | Date: 2020-03-08 14:44 | |

Thanks for the reference @Raymond. I could not find any mention of the union relationships for ≤ and ≥ though. Since they are always valid for Boolean values, in the same way that ≠ is always the complement of = for Boolean values and default implemented as such in the interpreter, what do you think of default implementing these union relationships in the interpreter? |
|||

msg364050 - (view) | Author: Géry (maggyero) * | Date: 2020-03-12 21:24 | |

Note that other relationships are always valid _and already implemented by default in the interpreter (through the `NotImplemented` return value protocol)_: = is the [converse](https://en.wikipedia.org/wiki/Binary_relation#Converse) of itself, ≠ is the converse of itself, < and > are each other’s converse, ≤ and ≥ as each other’s converse. ("converse" is loosely called "reflected" in the Python documentation.) Which also makes me think that the last sentence of this documentation paragraph is incorrect: > By default, `__ne__()` delegates to `__eq__()` and inverts the result > unless it is `NotImplemented`. There are no other implied > relationships among the comparison operators, for example, the truth > of `(x<y or x==y)` does not imply `x<=y`. since there _are_ other implied relationships besides ≠ is the complement of =: = is the converse of itself, ≠ is the converse of itself, < and > are each other’s converse, and ≤ and ≥ are each other’s converse. |
|||

msg364077 - (view) | Author: Géry (maggyero) * | Date: 2020-03-13 08:08 | |

More precisely: The following relationships are always valid and therefore implemented by default in Python (_except for the union relationships, which seems arbitrary and is the reason of this Python issue_): - 2 [complementary](https://en.wikipedia.org/wiki/Binary_relation#Complement) relationships: "= and ≠ are each other’s complement"; - 6 [converse](https://en.wikipedia.org/wiki/Binary_relation#Converse) relationships*: "= is the converse of itself", "≠ is the converse of itself", "< and > are each other’s converse", and "≤ and ≥ are each other’s converse"; - 2 [union](https://en.wikipedia.org/wiki/Binary_relation#Union) relationships: "≤ is the union < and =" and "≥ is the union of > and ≤". The following relationships are only valid for [total orders](https://en.wikipedia.org/wiki/Binary_relation#Properties) and therefore not implemented by default in Python (but users can conveniently implement them when they are valid with the [`functools.total_ordering`](https://docs.python.org/3/library/functools.html#functools.total_ordering) class decorator provided by the Python standard library): - 4 [complementary](https://en.wikipedia.org/wiki/Binary_relation#Complement) relationships: "< and ≥ are each other’s complement" and "> and ≤ are each other’s complement". ---- \* Converse relationships are implemented in Python through the [`NotImplemented` protocol](https://docs.python.org/3/reference/datamodel.html#the-standard-type-hierarchy). |
|||

msg377536 - (view) | Author: Irit Katriel (iritkatriel) * | Date: 2020-09-26 17:55 | |

The PEP that Raymond linked to says: "Further smarts could have been added to the comparison mechanism, but this limited set of allowed "swaps" was chosen because it doesn't require the infrastructure to do any processing (negation) of return values. The choice of six special methods was made over a single (e.g. __richcmp__) method to allow the dispatching on the opcode to be performed at the level of the C implementation rather than the user-defined method." The pseudo code you suggested assumes that the results of comparisons can be interpreted as booleans, which not always correct and makes your suggestion under-specified. It is not easy to devise a sound and intuitive composition of boolean expressions whose semantics are user-defined. As an aside, I think for the boolean case it is enough that one of them is not NotImplemented, so your pseudo code should have been: def __le__(self, other): result_1 = self.__lt__(other) result_2 = self.__eq__(other) if result_1 is NotImplemented and result_2 is NotImplemented: return NotImplemented return result_1 or result_2 |
|||

msg377538 - (view) | Author: Raymond Hettinger (rhettinger) * | Date: 2020-09-26 18:53 | |

This issue is more of a question or objection than a bug report, so I'm going to mark this as closed. Feel free to continue the discussion on python-ideas or ask more about it on StackOverflow. |
|||

msg377563 - (view) | Author: Géry (maggyero) * | Date: 2020-09-27 12:18 | |

Alright @rhettinger, here is the post: https://discuss.python.org/t/add-missing-default-implementations-of-le-and-ge/5327 |

History | |||
---|---|---|---|

Date | User | Action | Args |

2022-04-11 14:59:27 | admin | set | github: 84043 |

2020-09-27 12:18:02 | maggyero | set | messages: + msg377563 |

2020-09-26 18:53:03 | rhettinger | set | status: open -> closed resolution: not a bug messages: + msg377538 stage: resolved |

2020-09-26 17:55:27 | iritkatriel | set | nosy:
+ iritkatriel messages: + msg377536 |

2020-03-13 08:08:47 | maggyero | set | messages: + msg364077 |

2020-03-12 21:24:09 | maggyero | set | messages: + msg364050 |

2020-03-08 14:44:20 | maggyero | set | messages: + msg363665 |

2020-03-05 16:49:42 | rhettinger | set | nosy:
+ rhettinger messages: + msg363442 |

2020-03-05 10:29:41 | maggyero | create |