Welcome to the Treehouse Community
Looking to learn something new?
Why do comparisons work even though we do not convert "other" to an int?
In this video, we are using magic methods to compare the values of various dice.
For example, consider this method:
def __eq__(self, other): return int(self) == other
This method allows us to check whether or not two die are equal in value.
In the method, we need to convert our object to an
int to do the comparison, but there is no such requirement for the
other parameter. Yet, somehow, python successfully can do the comparison. The same goes for all the other comparison methods we define in this video.
My question is, how does python manage to calculate a value for
other in order to do the comparison successfully? How does it know that it is comparing two ints, or how does it know to convert
other to an int before converting? I am sure that this is some "magic" going on behind the scenes, but would someone be able to illuminate for me what is going on?
The video I am referring to this: https://teamtreehouse.com/library/comparing-and-combining-dice
Chris FreemanTreehouse Moderator 68,154 Points
The comparison operation is reflexive. Given,
a == b, Python will automatically try
b == a if needed.
d2 are instances of
D6, the flow would be:
d1 = D6(value= 3) d2 = D6(value= 6) d1 == d2 # call d1.__eq__ int(d1) == d2 3 == d2 # reverse d2 == 3 # call d2.__eq__ int(d2) == 3 6 == 3
This action can be seen in the code
import random class Die: def __init__(self, sides=2, value=0): print("running Die.__init__") if not sides >= 2: raise ValueError("Must have at least 2 sides") if not isinstance(sides, int): raise ValueError("Sides must be a whole number") self.value = value or random.randint(1, sides) def __int__(self): print("running Die.__int__") return self.value class D6(Die): def __init__(self, value=0): print("running D6.__init__") super().__init__(sides=6, value=value) def __eq__(self, other): print("running D6.__eq__", self, other) return int(self) == other def __ne__(self, other): print("running D6.__ne__") return int(self) == other def __gt__(self, other): print("running D6.__gt__") return int(self) > other def __lt__(self, other): print("running D6.__le__") return int(self) < other def __ge__(self, other): print("running D6.__ge__") return int(self) > other or int(self) == other def __le__(self, other): print("running D6.__le__") return int(self) < other or int(self) == other def __add__(self, other): print("running D6.__add__", self, other) return int(self) + other def __radd__(self, other): print("running D6.__radd__ with int(self)", self, other) return int(self) + other d1 = D6(value= 3) print('d1', d1) d2 = D6(value= 6) print('d2', d2) print(d1 == d2)
running D6.__init__ running Die.__init__ d1 <__main__.D6 object at 0x1163fcb38> running D6.__init__ running Die.__init__ d2 <__main__.D6 object at 0x11640b320> running D6.__eq__ <__main__.D6 object at 0x1163fcb38> <__main__.D6 object at 0x11640b320> running Die.__int__ running D6.__eq__ <__main__.D6 object at 0x11640b320> 3 running Die.__int__ False
When an object is on the right-side of an operator a reflexive method is tried:
__radd__ instead of
For comparison operators, there isn’t an explicit corresponding reflexive method.
From the docs
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,
__gt__()are each other’s reflection,
__ge__()are each other’s reflection, and
__ne__()are their own reflection.
3 > d2 would actually go
3 > d2 # reverse d2 < 3 # call d2.__lt__ int(d2) < 3 6 < 3
running D6.__le__ running Die.__int__
Okay, here is what I am understanding from this:
1) First, the object on the left is reduced to a primitive data type 2) The appropriate reflexive method for the object on the right, as defined in its own class code, is called to reduce it to a primitive data type. 3) The two primitive datatypes are compared to give the final result
Is that right?
I see from this that it tries the equation one way, goes as far as it can until it ends up with a primitive value, then flips it and goes through again until it is comparing two primitive values.
That makes sense for an easily reversible comparison, like ==, but what about < or >, where which side of the operator you are on matters?
Great! Thanks for your help, Chris!