Tip
Here are the two basic rules
First, the operand on the left is checked for an operator implementation: A<B
means A.__lt__(B)
.
Second, the operand on the right is checked for a reversed operator implementation: A<B
means B.__gt__(A)
.
The rare exception to this occurs when the right operand is a subclass of the left operand; then, the right operand is checked first to allow a subclass to override a superclass.
We can see how this works by defining a class with only one of the operators defined and then using it for other operations.
The following is a partial class that we can use:
class BlackJackCard_p: def __init__( self, rank, suit ): self.rank= rank self.suit= suit def __lt__( self, other ): print( "Compare {0} < {1}".format( self, other ) ) return self.rank < other.rank def __str__( self ): return "{rank}{suit}".format( **self.__dict__ )
This follows the Blackjack comparison rules where suits don't matter. We've omitted comparison methods to see how Python will fallback when an operator is missing. This class will allow us to perform the <
comparisons. Interestingly, Python can also use this to perform the >
comparisons by switching the argument order. In other words, x<y≡y>x. This is the mirror reflection rule; we'll see it again in Chapter 7, Creating Numbers.
We see this when we try to evaluate different comparison operations. We'll create two Cards
classes and compare them in various ways as shown in the following code snippet:
>>> two = BlackJackCard_p( 2, '♠' ) >>> three = BlackJackCard_p( 3, '♠' ) >>> two < three Compare 2♠ < 3♠ True >>> two > three Compare 3♠ < 2♠ False >>> two == three False >>> two <= three Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unorderable types: BlackJackCard_p() <= BlackJackCard_p()
From this, we can see where two < three
maps to two.__lt__(three)
.
However, for two > three
, there's no __gt__()
method defined; Python uses three.__lt__(two)
as a fallback plan.
By default, the __eq__()
method is inherited from object
; it compares the object IDs; the objects participate in ==
and !=
tests as follows:
>>> two_c = BlackJackCard_p( 2, '♣' ) >>> two == two_c False
We can see that the results aren't quite what we expect. We'll often need to override the default implementation of __eq__()
.
Also, there's no logical connection among the operators. Mathematically, we can derive all the necessary comparisons from just two. Python doesn't do this automatically. Instead, Python handles the following four simple reflection pairs by default:
This means that we must, at the minimum, provide one from each of the four pairs. For example, we could provide __eq__()
, __ne__()
, __lt__()
, and __le__()
.
The @functools.total_ordering
decorator overcomes the default limitation and deduces the rest of the comparisons from just __eq__()
and one of these: __lt__()
, __le__()
, __gt__()
, or __ge__()
. We'll revisit this in Chapter 7, Creating Numbers.