Locked learning resources

Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Locked learning resources

This lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson


00:00 So, how does comparison by equality actually work? Well, the secret is the .__eq__() class method, which I’ll just call the equals class method. And it actually has a counterpart, the .__ne__(), or not equals, class method.

00:15 I’ll talk about both of those when I move into the terminal in a minute or two. So, the == operator actually calls this .__eq__(), equals, method under the hood, and the implementations, of course, have to vary based on the requirements of the object. An integer has to have numeric comparison, whereas a string has to compare each individual character to see if all of them match, and only then are the two strings evaluated as equal.

00:41 And of course, a custom object might have a totally different standard of what constitutes equality, you know? Maybe you’re doing some astronomical calculations or something, and you want to know whether you might consider a comet equal if it maintains the same distance from a planet or something—and please, astronomers, don’t come after me.

01:01 I don’t actually have any clue what I’m talking about. But that’s something I imagine you might be interested in doing.

01:08 What’s important to know about the actual implementation of it is that the .__eq__() method of the left-hand operand, so a in this little diagram—that is the .__eq__() method that will be called when you evaluate this == operator on the two.

01:23 So a’s .__eq__() method will be called. Unless, of course, the right-hand operand is a subclass of the left-hand operand. If one of these two is a subclass of the other, then that is always the .__eq__() method that will be called because, generally in object-oriented programming—and you can read more about this on Real Python—you want your subclasses to have more specific methods than their superclass, and so it makes sense for the subclass’s method to be the one that’s called.

01:52 Let’s move into the terminal and take a look at how this works in practice. What I’m going to do is I’m going to define a class called a SillyString, and it’s going to be a subclass of str (string), and you’ll see why it’s silly in a second.

02:03 So, we’ve got defining an .__eq__() method here, and it’s going to compare itself to another object. And so this .__eq__() method—first, I’m just going to have it print out a couple of interesting things.

02:16 It’s going to say f"Comparing {self} to {other}".

02:24 It’s just going to kind of say what it’s doing. And then it’s going to say return—uh, I’m not sure. Oh, I know why this is happening. Because I forgot to close the quotes!

02:34 Then it’s going to just return whether its length is equal to the length of the other thing. And of course, this is a silly way to calculate equality, but you can imagine a thing where you might want to compare whether strings’ lengths are equal, and you might want to just have its own class for that.

02:52 It’s kind of a teaching example, but you could see why you might want something like it at least. So, of course, normal strings, you would say 'hello' == 'world'. That’s False.

03:03 And 'hello' != 'world' is True.

03:07 But if you do SillyString('hello'),

03:12 if you do that == 'world', then it compares 'hello' to 'world' and it gets True, because their length is the same. And it doesn’t matter—you could do that or you could have

03:24 SillyString('world') and both the—

03:29 oh! I’m sorry, I used the wrong thing here. Silly me. 'hello', come back here! All right. My point that I failed to make because I highlighted the wrong thing was just that if the SillyString is on the left or if it’s on the right—it doesn’t matter because SillyString is a subclass of str, and so the subclass, that .__eq__() method will always be called, right?

03:52 So now, what would happen if I did… I’ll show you, kind of, the silliness here, is that you could also just have a list with five elements, and you’d actually get the same True value because it doesn’t do any type checking. It just says “Are the lengths of these two things the same?” So that’s kind of a pitfall with equality, is that the actual string equality function is rather complex, and you might—you’ll have to do a good amount of work, is what I’m saying, if you want to do a custom equality function for one of your classes.

04:26 But that’s something that you probably will have to do at some point. Now, something to note is that the .__ne__() (not equals) function here will not work as we expect because I failed to implement a .__ne__() function in my SillyString class.

04:45 So this .__ne__() will actually still evaluate to True, and the reason for that is because it’s using the superclass’s .__ne__(), which is string comparison. So it’s saying, “Are these strings not equal to each other?” And that is True, because the string comparison returns False on these two, right?

05:02 And so if you want a .__ne__() for a subclass, you’ll probably have to implement one yourself. Luckily, that’s pretty simple to do. You can just actually return the negation of the .__eq__().

05:16 So return self.__eq__() on the other, but the negation of that. And if that’s the case, then this will in fact compare as we expect it to, right?

05:29 And so all this to say that there are a lot of pitfalls to implementing custom equality and not equality methods, but these are the two methods which govern how these operators work when you compare objects.

05:41 And so review what I’ve said in this video and try to maybe come up with an example or two on your own and see how this kind of has some pitfalls. And luckily, if you implement something that’s not a subclass, then the not equals operator (!=) will default to inverting the equals operator (==), so doing exactly what I did here. But if it’s a subclass, that’s not guaranteed.

06:02 So these things can be tricky. But those are the .__eq__() and .__ne__() double underscore methods which perform the actual functions of these operators.

06:11 One last thing I want to cover real quick is why exactly in previous videos I’ve said that you should use is to compare with None. Well, if I say SillyString('world') and I want to compare it with None, if I say is None, then I get False just as I expect.

06:31 And likewise with is not None, which returns True. But if I say == None, I get an error because 'NoneType' has no len(), and if you remember from the definition that .__eq__() and .__ne__() both rely on the length parameter of the object in question.

06:51 And so you can imagine too that maybe if my SillyString did a lot of expensive computations with .__eq__(), it would also be just faster to use is None because you’re just comparing these memory IDs. But in this case, it actually generates an error, and so it makes your programming more difficult because you have to account for the None case if you assume that people are going to use equality to compare it to None. So it’s much safer and faster to use is to compare to None.

07:16 And that’s just a little aside of me talking about these double under (__) methods.

Avatar image for Zarata

Zarata on Aug. 11, 2020

I like your presentations BTW – great content, style, clarity. That said, I found the mention of “Calls the method on the left hand operand … unless …”, a little challenging, i.e. “Now how does that work?”. I didn’t remember or hadn’t picked up the “rules” from the ‘other’ (sick recursive use of the word) tutorials on subclassing re: resolution as to which object is assigned to the “self” and to the “other” arguments of a dunder when a dunder backs some operator in an expression. I guess I’ll pull up the REPL to see what happens with SillyString("a") == SillyString("b")

Avatar image for Liam Pulsifer

Liam Pulsifer RP Team on Aug. 11, 2020

Thanks for the compliment, @Zarata! These resolution rules can be quite complex, and I don’t pretend to have them fully committed to memory. I think playing around in the REPL is a great solution, and if that doesn’t answer all of your questions, I would head straight to the documentation here and look around there.

Avatar image for Harry Cheng

Harry Cheng on May 14, 2023

Still didn’t quite get it: 1. For str == SillyString equal test, although SillyString is on the right, the eq of SillyString is called. In the past I thought it is the same as str.eq(SillyString). The explanation is they are both subclass of str? 2. For SillyString == list equal test. The eq of SillyString is used, not list’s. How can I wrap my head around about this? I think this can not be explained by the subclass rule?

Become a Member to join the conversation.