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 trial

Python

Ronald Lira
Ronald Lira
12,217 Points

How does this work int(self) == other ?

Hello,

I know this works, but I would like to understand how does the comparison at int(self) == other work? , because on the right side we are using the int version of the object (this will bring self.value), but on the left we are just using the object itself. How does python know it has to use also other.value.

import random

class Die:
    def __init__(self, sides=2, value=0):
        if not sides >= 2:
            raise ValueError("Must have at least 2 sides")
        if not isinstance(sides, int):
            raise TypeError("Sides must be an integer value")

        self.value = value or random.randint(1,sides)

    def __int__(self):
        return self.value

    def __eq__(self, other):
        return int(self) == other

In my mind it would make sense the following:

    def __eq__(self, other):
        return int(self) == int(other)

Thank you

3 Answers

Steven Parker
Steven Parker
229,644 Points

I believe that since you've made the first term into an int, then the system automatically tries to convert the second term into the same type for you. Since we know this type can be converted to int, then it will work if the second item is also a Die. But if the second item is some other type, we don't want to force a particular type of conversion.

Johannes Scribante
Johannes Scribante
19,175 Points

Hi Ronald,

I hope I can answer your question. There are actually three parts to your question.

  1. self.value: In this part of the code you define a class Die and you assign the Die a value of 5 as an example. Now I assign a variable d5 = Die() to create a d5 variable which is of the Die class. The d5 now has a value, however if I would like to know what that value is, I would have to use d5.value.
class Die:
    def __init__(self, sides=2, value=0):
        if not sides >= 2:
            raise ValueError("Must have at least 2 sides")
        if not isinstance(sides, int):
            raise TypeError("Sides must be an integer value")

        self.value = 5  # assigning the value 5 just as an example
  1. __int__(self): Next is the magic method __int__, here you are setting the Die classes __int__ method to be custom, i.e. you could do what ever you want every time you would use int(d5) for example. However we would like the int(d5) to return an integer value of die d5 die which is of the class Die. If you did not specify what int(d5) would do, Python would not know what to do when int() is called on the Die class, Python does not know what the Die class is or what values or methods for part of the class. You have to explicitly tell Python what you mean when you want to use the method int(Die) on the Die class. In this case below we are telling Python, when we use the method int(Die) on a class of Die we want to return the value of the Die instance.
def __int__(self):
        return self.value
  1. __eq__: Finally the one you want to know about. So now that we know our Die class contain a value (d5.value = 5, from 1.), we know the if I were to use the method int(d5) we would get 5 to be returned. Now finally we want to be able to test d5 == 2 or maybe d5 == 5 to get true of false (in this case False and True). We don't want to have to say int(d5) == 2 or int(d5) == 5 so we include this in our __eq__ method. When we type Die == other_value we are calling the __eq__(Die, other_value) method passing the Die instance and the other_value. Now the __eq__ method calls our __int__ method (from 2.) to return the value of the Die instance and compares it to the value of other_value and returns the True or False, just as if we were you compare 2 == 3. We are making it easier for ourselves to just be able to use d5 == 5 instead of int(d5) == 5 or d5.value == 5
def __eq__(self, other):
        return int(self) == other

I hope it makes sense, thanks for asking, it's a good question! Good luck!

Johannes Scribante
Johannes Scribante
19,175 Points

Just one thing to add that I realized.

The big reason for using return int(self) == other and not return int(self) == int(other) is because we know that the Die class will only have integer values, no float values, as a Die cannot have 5.2 sides as an example.

In the same way you want to be explicit in saying that the value you are equating the Die to, should not be altered in any way. Otherwise you would be able to equate Die to integers and floats. If you were to use return int(self) == int(other), you would get the following result, given that d5.value = 5.

d5 == 5.3  # True
d5 == 5  # True
Ronald Lira
Ronald Lira
12,217 Points

Hello @stevenparker and @johannesscribante. Thank you very much for your answers. I found a little more insight with this post: https://stackoverflow.com/questions/3588776/how-is-eq-handled-in-python-and-in-what-order

If we have:

d1 = D6()
d2 = D6()

d1 == d2

Python invoke d1.__eq__(d2). The code returns self.value == other but self.value is an int object and python doesnt know how to compare int to D6 objects, so python invokes now d2.__eq__(int) which is defined because type(d1) == type(d2)

I would like to note that it also depends on the functionality you want to provide. In this case, we want to:

  1. Compare a Die object directly to another Die object
  2. Compare a Die object to any int object.

And this code does that exactly. But in general, I would like to compare only apples to apples and oranges to oranges, and if the user tries to compare apple to oranges an exception should be raised or at least return False. Maybe something like:

 def __eq__(self, other):
      if isinstance(other, self.__class__):
            return self.value == other.value
      return False

But I am not sure if there is a more elegant way to do this without having to check for the class that other belongs to on every magic method.

Thank you

Steven Parker
Steven Parker
229,644 Points

You could make an internal method to compare object types to avoid repeating the same test. But you probably wouldn't need to do this kind of thing often. It's more likely you would provide an int() conversion because you want to be able to compare to ordinary integers.