Python '!=' Is Not 'is not': Comparing Objects in Python (Summary)
In this course, you learned that ==
and !=
compare the value of two objects, whereas the Python is
and is not
operators compare whether two variables refer to the same object in memory. If you keep this distinction in mind, then you should be able to prevent unexpected behavior in your code.
If you want to read more about the wonderful world of object interning and the Python is
operator, then check out Why you should almost never use “is” in Python. You could also have a look at how you can use sys.intern()
to optimize memory usage and comparison times for strings, although the chances are that Python already automatically handles this for you behind the scenes.
Now that you’ve learned what the equality and identity operators do under the hood, you can try writing your own __eq__()
class methods, which define how instances of this class are compared when using the ==
operator. Go and apply your newfound knowledge of these Python comparison operators!
Congratulations, you made it to the end of the course! What’s your #1 takeaway or favorite thing you learned? How are you going to put your newfound skills to use? Leave a comment in the discussion section and let us know.
00:00
This last lesson will just be a quick review and conclusion and I’ll do a nice kind of comparison of the Python comparison operators. So, is
and is not
versus double equals (==
) and not equals (!=
). Well, the long and short of it is that you should use ==
if you want to compare the contents of two objects, so the actual value of the objects themselves, right?
00:24
So, a string is the actual characters in the string, an integer is the actual numeric value of the integers, and so on. And this ==
or !=
relies on the .__eq__()
method of the objects being compared or the .__ne__()
, not equal, method if you are being really careful about it. And remember the ordering, which is that the left-hand operand’s .__eq__()
method is actually the one called when you use the ==
operator, unless one of the two objects is a subclass of the other, in which case that subclass’s .__eq__()
method will be used.
01:03
And also just remember that this .__eq__()
method is really awesome and cool to use when you’re defining your own objects, but you have to be careful to make sure that you also know what the behavior of .__ne__()
will be.
01:14
It’s kind of difficult to do if you’re using a subclass relationship, so just be careful and make sure that you test well for that. is
and is not
should be used to compare the memory addresses of two objects, or to compare with None
. So, is
is strictly to compare the IDs, the Python memory addresses. And if you’re not actually wanting to compare that, then you shouldn’t use it in any circumstance.
01:40
A lot of people use the wisdom, they just say, “Compare with None
using is
or is not
, and don’t compare anything else with it.” I would see it a little more lenient and say that there might be cases where you want to compare the memory addresses of objects, so if you want to do that, then use that nomenclature. Otherwise, stick with .__eq__()
.
02:00
Let’s move over into the terminal for just a super quick final review. I think the best way to do it is to just go back to the same example I’ve been using this whole time. So a
and b
, "This is a string"
, a == b
is True
but the IDs of a
and b
are different because they were declared differently, and so the a is b
expression actually evaluates to False
.
02:24
And just to show you the inverses, a != b
is False
and a is not b
—that is in fact True
. And remember the interning, which is from sys import intern
.
02:38
And so you can, if you intern your strings, then you can actually have this a is b
evaluate to True
because intern()
makes all references with that same value point to the same spot in memory.
02:53
That happens by default with small integers, and so x
and y
, if I initialize them both to 4
, then x is y
evaluates to True
.
03:03
But Python 3.8 and higher are very clever, and so if you say something like x is 7
, it will give you a SyntaxWarning
, which I think is very valuable and meaningful.
03:13
So just don’t compare this way with numeric values and that sort of thing. And remember that you can define classes with the .__eq__()
method.
03:21
So, class SillyString(str):
03:25
and it has an .__eq__()
method,
03:29
which takes in its self
and the other
, and just returns len(self) == len(other)
.
03:38
But remember that while this SillyString
has cool behavior
03:44 with this kind of stuff—
03:48
this evaluates to True
—it won’t actually have the inverse behavior with .__ne__()
unless you actually implement .__ne__()
. You would do that by saying __ne__(self, other):
and you would just return not self.__eq__(other)
.
04:08
And if that’s the case, then you get the actual desired behavior here of False
. So, those are the examples that I used in this, and I hope that they were helpful to you. I hope you enjoyed the course.
Liam Pulsifer RP Team on April 3, 2020
Great to hear, Kevin! Glad you got a chance to learn something new :)
bday29 on April 9, 2020
I was following along in pycharm and the variables, “a” and “b” when set to, “This is a string”, are equal when using the is comparison operator. I am guessing that pycharm intern’s string automagically for us or something.
bday29 on April 9, 2020
I thought these lessons were very helpful, thank you.
Liam Pulsifer RP Team on April 9, 2020
Interesting @bday29. I suppose that could be possible – pycharm does a lot of cool optimizations under the hood. Glad you found the lessons helpful otherwise!
d0ctajones on April 22, 2020
Thanks for the course, Liam! Very helpful in explaining the differences. Had never heard of “intern” before so I did a little research. Based on this excellent Medium article, string interning follows a few simple rules:
- The string must be a compile-time constant
- The string must be not be subject to constant folding or no longer than 20 characters
- The string consists exclusively of ASCII letters, digits, or underscores
When I was following along with experimenting with the interning example #3 is the one that tricked me. I was using short strings with no spaces and it seemed like they were getting automatically interned. This was true! Spaces in the string disqualify it from being automatically interned. is
statements worked like a charm on the short, space-less strings.
Hope this helps someone else who’s learning and is too lazy to type longer strings. Thanks again.
Liam Pulsifer RP Team on April 30, 2020
Thanks for that super informative comment @d0ctajones! I wasn’t even aware of all of the specifics of the third rule, so you’ve taught me something today :)
paolotagliente on July 10, 2020
Really liked the way you explained this stuff, i liked also the approach of going over a few times some concepts and at good pace.....i like the colour of the screen where the commands popped out nicely....good work....i am pretty new to python and i got to say that i really like your way to cover and teach stuff…thanks!
Liam Pulsifer RP Team on July 11, 2020
Really glad I could be of help @paolotagliente! Stay tuned for some new tutorials I’ll have out in the coming weeks :)
anders stenborg martin on July 16, 2020
Good course. Looking forward to new tutorials from you!
Zarata on Aug. 11, 2020
Ditto the comments above, Liam.
As you’ve implied, and just to mention to other newbies like me: “it’s complicated”. E.g. the overwrite of __eq__
has implications on the hash-a-bility of an object such as the example user-defined SillyString. Other RP tutorials point to stackoverflow.com/questions/14535730/what-does-hashable-mean-in-python and eng.lyft.com/hashing-and-equality-in-python-2ea8c738fb9d. The first states “Objects which are instances of user-defined classes are hashable by default; they all compare unequal, and their hash value is their id().” Actually, it’s id()>>4
. However, hash(SillyString('a'))
returns for me TypeError: unhashable type: 'SillyString'
. The simple overwrite of __eq__
in SillyString
breaks an __eq__
, __hash__
contract honored in the default. There be dragons here!
Ghani on Oct. 15, 2020
Very good course; thanks!
hemant-technerd on Nov. 26, 2021
I have created a subclass
from str
class as below:
class SillyString(str): # SillyString subclass of str class
def __eq__(self, other):
print(f"Comparing {self} to {other}")
return len(self) == len(other) #silly comparison
Now, I am creating the objects and printing output as below:
s1 = "hello"
s3 = SillyString("world")
print('{0} == [SillyString] {1}'.format(s1, s3), s1 == s3)
My output looks as below:
Comparing world to hello
hello == [SillyString] world True
Please help me understand why Comparing world to hello
is printing before the hello == [SillyString] world
.
Also why hello == [SillyString] world
is printed out before True
?
Bartosz Zaczyński RP Team on Nov. 27, 2021
@hemant-technerd The comparison operator in s1 == s3
makes your SillyString.__eq__()
method to run first. Since this method contains a call to print()
in its body, it prints out a message “Comparing (…)” prior to returning a Boolean result. That’s why you’re seeing it before your other print statement.
On the other hand, the order of values printed out later is determined by order of arguments to print()
.
Become a Member to join the conversation.
Kevin Dienst on April 1, 2020
I learned about interning! I had encountered this concept before but wasn’t aware of the lower and upper bounds (-5 to 256), nor was I aware of using the intern() method itself. Pretty cool!