Welcome to the Treehouse Community
Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.
Looking to learn something new?
Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.
Start your free trialzy93
Python Web Development Techdegree Student 6,879 PointsWhy 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
4 Answers
Chris Freeman
Treehouse Moderator 68,423 PointsThe comparison operation is reflexive. Given, a == b
, Python will automatically try b == a
if needed.
If d1
and 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)
Output:
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
Update:
When an object is on the right-side of an operator a reflexive method is tried: __radd__
instead of __add__
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,
__lt__()
and__gt__()
are each other’s reflection,__le__()
and__ge__()
are each other’s reflection, and__eq__()
and__ne__()
are their own reflection.
So, comparing 3 > d2
would actually go
3 > d2 # reverse
d2 < 3 # call d2.__lt__
int(d2) < 3
6 < 3
running D6.__le__
running Die.__int__
zy93
Python Web Development Techdegree Student 6,879 PointsOkay, 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?
zy93
Python Web Development Techdegree Student 6,879 PointsI 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?
Chris Freeman
Treehouse Moderator 68,423 PointsAnswer expanded above.
zy93
Python Web Development Techdegree Student 6,879 PointsGreat! Thanks for your help, Chris!
Steven Parker
229,644 PointsSteven Parker
229,644 PointsGolly, that's more complicated than coercion! I expected that it did it the simpler way.