The problem is a typo: ___le__() should be __le__().

However, that's a very unusual way to implement comparison operators. Usually you compare two objects of the same type instead of comparing a number to a Book object. That's why this was so confusing: the > operator is actually calling the __lt__() method, and the >= doesn't find a __le__() method. The reason the direction is reversed is that the number on the left side of of the comparison operator doesn't implement rich comparison methods, but the Book on the right does. That causes the reversed comparison method to get called.

There are no swapped-argument versions of these methods (to be used when the left argument does not support the operation but the right argument does); rather, __lt__() and __gt__() are each other’s reflection, __le__() and __ge__() are each other’s reflection, and __eq__() and __ne__() are their own reflection.

I think it would be much easier to understand if the class just implemented __cmp__().

Answer from Don Kirkby on Stack Overflow
🌐
GeeksforGeeks
geeksforgeeks.org › python-pandas-series-ge
Python | Pandas Series.ge() | GeeksforGeeks
March 1, 2020 - Python is a great language for ... and analyzing data much easier. Pandas series.ge() is used to compare every element of Caller series with passed series....
🌐
Finxter
blog.finxter.com › home › learn python blog › python __ge__() magic method
Python __ge__() Magic Method - Be on the Right Side of Change
November 22, 2021 - Short summary: To customize the behavior of the greather than or equal to operator x >= y, override the __ge__() dunder method in your class definition. Python internally calls x.__ge__(y) to obtain a return value when comparing two objects using x >= y. The return value can be any data type ...
Discussions

Python operator overriding: __ge__ result is not as expected - Stack Overflow
Problem about Python operator overriding: __ge__ (corresponding to '>=') result is not as expected class Book: title = '' pages = 0 def __init__(self, title='', pages=0): self... More on stackoverflow.com
🌐 stackoverflow.com
July 5, 2016
python operator overloading implement: __ge__ using __gt__ and __eq__ - Stack Overflow
I am overloading the operator methods. Is it ok to implement __ge__ using __gt__ and __eq__ ? Why is it good/bad practice ? def __gt__(self, other): pass def __eq__(self, other): pass def __ge_... More on stackoverflow.com
🌐 stackoverflow.com
relationship - Why doesn’t Python provide default implementations of __le__ and __ge__? - Stack Overflow
Why is Python only lacking the 2 union relationships above ("≤ is the union < and =" and "≥ is the union of > and =")? It should provide a default implementation of __le__ in terms of __lt__ and __eq__, and a default implementation of __ge__ in terms of __gt__ and __eq__, like these (but ... More on stackoverflow.com
🌐 stackoverflow.com
Add missing default implementations of __le__ and __ge__ - Ideas - Discussions on Python.org
The following mathematical relationships between comparison relations (=, ≠, , ≤ and ≥) are always valid and therefore implemented by default in Python (except for the 2 union relationships, which is the reason of this post): 2 complementary relationships: “= and ≠ are each other’s ... More on discuss.python.org
🌐 discuss.python.org
0
September 27, 2020
🌐
IncludeHelp
includehelp.com › python › operator-ge-function-with-examples.aspx
Python operator.ge() Function with Examples
April 14, 2020 - operator.ge() function is a library function of operator module, it is used to perform "greater than or equal to operation" on two values and returns True if the first value is greater than or equal to the second value, False, otherwise. ... The return type of this method is bool, it returns ...
🌐
W3Schools
w3schools.com › python › pandas › ref_df_ge.asp
Pandas DataFrame ge() Method
The ge() method compares each value in a DataFrame to check if it is greater than, or equal to a specified value, or a value from a specified DataFrame objects, and returns a DataFrame with boolean True/False for each comparison.
🌐
Pythontic
pythontic.com › pandas › dataframe-binaryoperatorfunctions › ge
ge() Function Of Pandas DataFrame Class | Pythontic.com
The instance method DataFrame.ge() of Python pandas library, implements the relational binary operation “Greater than or equal to”(>=).
🌐
Python
docs.python.org › 3 › library › operator.html
operator — Standard operators as functions
1 week ago - def methodcaller(name, /, *args, **kwargs): def caller(obj): return getattr(obj, name)(*args, **kwargs) return caller · This table shows how abstract operations correspond to operator symbols in the Python syntax and the functions in the operator module.
Find elsewhere
🌐
w3resource
w3resource.com › pandas › dataframe › dataframe-ge.php
Pandas DataFrame: ge() function - w3resource
The ge() function returns greater than or equal to of dataframe and other, element-wise.
🌐
PyPI
pypi.org › project › ge
ge · PyPI
The ge package provides utility functions for evaluating and constructing formulas for fitted models, particularly useful in the context of machine learning and data analysis.
      » pip install ge
    
Published   Jun 03, 2025
Version   0.0.5
Top answer
1 of 3
8

Why exactly this decision was made only the original author knows, but given these hints from the manual reasons can be inferred:

To automatically generate ordering operations from a single root operation, see functools.total_ordering().

While this decorator makes it easy to create well behaved totally ordered types, it does come at the cost of slower execution and more complex stack traces for the derived comparison methods. If performance benchmarking indicates this is a bottleneck for a given application, implementing all six rich comparison methods instead is likely to provide an easy speed boost.

Pair this with Python's mantra of explicit is better than implicit, the following reasoning should be satisfactory:

Deriving __ne__ from __eq__ is virtually free, it's just the operation not o.__eq__(other), i.e. inverting a boolean.

However, deriving __le__ from the union of __lt__ and __eq__ means that both methods need to be called, which could be a potentially large performance hit if the comparison done is complex enough, especially compared to an optimised single __le__ implementation. Python lets you opt-into this convenience-over-performance explicitly by using the total_ordering decorator, but it won't implicitly inflict it on you.

You could also argue for explicit errors if you attempt to do unimplemented comparisons instead of implicitly derived comparisons which you didn't implement and which may create subtle bugs, depending on what you meant to do with your custom classes. Python won't make any guesses for you here and instead leave it up to you to either explicitly implement the comparisons you want, or to again explicitly opt-into the derived comparisons.

2 of 3
6

Your question is based on a number of incorrect assumptions. You start your question with:

The following mathematical relationships between comparison relations (=, , <, >, and ) are always valid and therefore implemented by default in Python (except for the 2 union relationships, which seems arbitrary and is the reason of this post).

There is no default implementation for < or > either. There is no default __lt__ or __gt__ implementation, so there can't be a default implementation for __le__ or __ge__ either.*

This is covered in the expressions reference documentation under Value Comparisons:

The default behavior for equality comparison (== and !=) is based on the identity of the objects. Hence, equality comparison of instances with the same identity results in equality, and equality comparison of instances with different identities results in inequality. A motivation for this default behavior is the desire that all objects should be reflexive (i.e. x is y implies x == y).

A default order comparison (<, >, <=, and >=) is not provided; an attempt raises TypeError. A motivation for this default behavior is the lack of a similar invariant as for equality.

The behavior of the default equality comparison, that instances with different identities are always unequal, may be in contrast to what types will need that have a sensible definition of object value and value-based equality. Such types will need to customize their comparison behavior, and in fact, a number of built-in types have done that.

The motivation to not provide default behavior is included in the documentation. Note that these comparisons are between the value of each object, which is an abstract concept. From the same documentation section, at the start:

The value of an object is a rather abstract notion in Python: For example, there is no canonical access method for an object’s value. Also, there is no requirement that the value of an object should be constructed in a particular way, e.g. comprised of all its data attributes. Comparison operators implement a particular notion of what the value of an object is. One can think of them as defining the value of an object indirectly, by means of their comparison implementation.

So comparisons are between one notion of the value of an object. But what that notion is exactly, is up to the developer to implement. Python will not assume anything about the value of an object. That includes assuming that there is any ordering inherent in the object values.

The only reason that == is implemented at all, is because when x is y is true, then x and y are the exact same object, and so the value of x and the value of y are the exact same thing and therefore must be equal. Python relies on equality tests in a lot of different places (like testing for containment against a list), so not having a default notion of equality would make a lot of things in Python a lot harder. != is the direct inverse of ==; if == is true when the values of the operands are the same, then != is only true when == is false.

You can't say the same for <, <=, => and > without help from the developer, because they require much more information about how the abstract notion of a value of the object needs to be compared to other similar values. Here, x <= y is not simply the result inverse of x > y, because there isn't any information about the values of x or y, and how that relates to == or != or < or any other value comparison.

You also state:

The 2 union relationships are always valid so these default implementations would free users from having to provide them all the time

The 2 union relationships are not always valid. It may be that the > and < operator implementation is not making comparisons and an implementation is free to return results other than True or False. From the documentation on the __lt__ etc. methods:

However, these methods can return any value, so if the comparison operator is used in a Boolean context (e.g., in the condition of an if statement), Python will call bool() on the value to determine if the result is true or false.

If an implementation decides to give > and < between two objects a different meaning altogether, the developer should not be left with incorrect default implementations of __le__ and __ge__ that assume that the implementation for __lt__ and __gt__ return booleans, and so will call bool() on their return values. This may not be desireable, the developer should be free to overload the meaning of __bool__ too!

The canonical example for this is the Numpy library, which was the primary driver for implementing these rich comparisons hooks. Numpy arrays do not return booleans for comparison operations. Instead, they broadcast the operation between all contained values in the two arrays to produce a new array, so array_a < array_b produces a new array of boolean values for each of the paired values from array_a and array_b. An array is not a boolean value, your default implementation would break as bool(array) raises an exception. While in Numpy's case they also implemented __le__ and __ge__ to broadcast the comparisons, Python can't require all types to provide implementations for these hooks just to disable them when not desired.

You appear to be conflating mathematical relationships with Python's use of some of those relationships. The mathematical relationships apply to certain classes of values (numbers, mostly). They do not apply to other domains, it is up to the implementation of each type to decide whether to honour those mathematical relationships.

Finally, the complementary relationship between < and >=, and between > and <= *only applies to total order binary relationships, as stated in the complement section of the Wikipedia article on binary relation:

For example, = and are each other's complement, as are and , and , and and , and, for total orders, also < and , and > and .

Python can't make the assumption that all type implementations wish to create total order relations between their values.

The standard library set type, for example, does not support total order between sets, set_a < set_b is true when set_a is a subset of a larger set_b. This means there can be a set_c that is a subset of set_b but set_c is not necessarily a subset, or superset of set_a. Set comparisons are also have no connexity, set_a <= set_b and set_b <= set_a can both be false, at the same time, when both sets have elements that are not present in the other.


* Note: the object.__lt__, object.__gt__, object.__le__ and object.__ge__ methods do have a default implementation, but only to return NotImplemented unconditionally. They exist only to simplify the implementation of the <, >, <= and >= operators, which for a [operator] b need to test a.__[hook]__(b) first, then try b.__[converse hook]__(a) if the first returns NotImplemented. If there was no default implementation, then the code would also need to check if the hook methods exist first. Using < or > or <= or >= on objects that do provide their own implementations results in a TypeError, nonetheless. Do not regard these as default implementations, they do not make any value comparisons.

🌐
Python.org
discuss.python.org › ideas
Add missing default implementations of __le__ and __ge__ - Ideas - Discussions on Python.org
September 27, 2020 - The following mathematical relationships between comparison relations (=, ≠, , ≤ and ≥) are always valid and therefore implemented by default in Python (except for the 2 union relationships, which is the reason of this post): 2 complementary relationships: “= and ≠ are each other’s complement”; 6 converse relationships*: “= is the converse of itself”, “≠ is the converse of itself”, “ are each other’s converse”, and “≤ and ≥ are each other’s converse”; 2 union relationships: “≤ is th...
🌐
O'Reilly
oreilly.com › library › view › python-in-a › 0596001886 › re05.html
__eq__, __ge__, __gt__, __le__, __lt__, __ne__ - Python in a Nutshell [Book]
Name__eq__, __ge__, __gt__, __le__, __lt__, __ne__Synopsis__eq__(self,other) __ge__(self,other) __gt__(self,other) __le__(self,other) __lt__(self,other)... - Selection from Python in a Nutshell [Book]
🌐
Medium
blog.cambridgespark.com › magic-methods-a8d93dc55012
Magic Methods. What are magic methods? They are… | by Cambridge Spark | Cambridge Spark
December 23, 2019 - It's usually better do define each ... since Python 3. __eq__(self, other) Defines the behaviour of the equality operator == __ne__(self, other) Defines the behaviour of the inequality operator != __lt__(self, other) Defines the behaviour of the less-than operator < __gt__(self, other) Defines the behaviour of the greater-than operator > __le__(self, other) Defines the behaviour of the less-than-or-equal-to operator <= __ge__(self, other) ...
🌐
ZetCode
zetcode.com › python › dunder-ge
Python __ge__ Method - Complete Guide
Complete guide to Python's __ge__ method covering greater than or equal comparison operator overloading.
🌐
geemap
geemap.org
geemap
Geemap is a Python package for interactive geospatial analysis and visualization with Google Earth Engine (GEE), which is a cloud computing platform with a multi-petabyte catalog of satellite imagery and geospatial datasets. During the past few years, GEE has become very popular in the geospatial ...
🌐
Scaler
scaler.com › home › topics › what are python dunder methods or magic methods?
What are Python Dunder Methods or Magic Methods?
May 4, 2023 - It returns a new object, which is then initialized by __init__(). The __ge__ method is the magical method that gets invoked when the >= operator is used and it returns True or False.
🌐
GitHub
github.com › gee-community › geemap
GitHub - gee-community/geemap: A Python package for interactive geospatial analysis and visualization with Google Earth Engine. · GitHub
Geemap is a Python package for interactive geospatial analysis and visualization with Google Earth Engine (GEE), which is a cloud computing platform with a multi-petabyte catalog of satellite imagery and geospatial datasets.
Starred by 3.9K users
Forked by 1.1K users
Languages   Python 86.0% | Jupyter Notebook 8.1% | TypeScript 5.0% | JavaScript 0.3% | TeX 0.2% | HTML 0.2%
🌐
Studytonight
studytonight.com › pandas › pandas-dataframe-ge-method
Pandas DataFrame ge() Method - Studytonight
March 17, 2021 - Python pandas DataFrame.ge() method is used to get greater than or equal to of dataframe and other, element-wise (binary operator get). It returns the DataFrame of bool type that is the result of the