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.
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.
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.
.__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.
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.
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.
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.
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?
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.
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
.__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: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.
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.
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.
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
.__ne__() both rely on the length parameter of the object in question.
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
Become a Member to join the conversation.