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.

classification
Title: Why are the union relationships not implemented by default for ≤ and ≥?
Type: enhancement Stage: resolved
Components: Interpreter Core Versions: Python 3.9
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: iritkatriel, maggyero, rhettinger
Priority: normal Keywords:

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) * (Python committer) 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) * (Python committer) 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) * (Python committer) 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:27adminsetgithub: 84043
2020-09-27 12:18:02maggyerosetmessages: + msg377563
2020-09-26 18:53:03rhettingersetstatus: open -> closed
resolution: not a bug
messages: + msg377538

stage: resolved
2020-09-26 17:55:27iritkatrielsetnosy: + iritkatriel
messages: + msg377536
2020-03-13 08:08:47maggyerosetmessages: + msg364077
2020-03-12 21:24:09maggyerosetmessages: + msg364050
2020-03-08 14:44:20maggyerosetmessages: + msg363665
2020-03-05 16:49:42rhettingersetnosy: + rhettinger
messages: + msg363442
2020-03-05 10:29:41maggyerocreate