#2 | Equality in Python
Overview
In python3:
-
Everything is an object.
-
Each object is stored at a memory address.
-
Two objects are different if and only if they have different memory addresses.
-
Use the identity operator
is
to check if two objects have the same memory address i.e. they are the same object. -
Use the equality operator
==
to check if two objects have the same value. -
For built in types like
int
andlist
the value used by==
is already defined so[1, 2] == [1, 2]
just works even though they are different objects. For objects created by custom classes the default value is the memory address meaningis
and==
will return the same thing unless a custom version of__eq__
is implemented. -
If
is
returnsTrue
then==
will returnTrue
. -
If
is
returnsFalse
then==
can returnTrue
orFalse
. -
If
==
returnsTrue
thenis
can returnTrue
orFalse
. -
If
==
returnsFalse
thenis
will also returnFalse
.
Examples
Objects from custom classes
Say we have a circle class and we create two instances.
Now let’s see what happens when we use is
and ==
to compare the two circles.
We get False
from is
because a
and b
point to different memory addresses and therefore two different objects. We get False
from ==
because the values of a
and b
are different. Hold on how does it assign a ‘value’ to an object? Well the default is id(object)
which just gives the memory address. So the check for equality here is the exact same for is
and ==
.
But we don’t have to use the default value for ==
.
Now we get True
for ==
because we’ve implemented the __eq__
function to say that the circles are equal if their color is the same. The __eq__
function of the object on the left hand side of ==
is used and if that returns NotImplemented
then the __eq__
function of the right hand object is used. If both return NotImplemented
then False
is returned overall.
Note also that there is nothing that forces __eq__
to return a boolean
. For example in the code below circle == square
returns apple
.
Built in types
For built in types such as int
, float
and list
the value used by ==
to determine equality is predefined and just makes sense.
Let’s look at the behaviour of is
.
A slight surprise is that when a
and b
are assigned the same value ("a"
in this case) then is
returns True
which doesn’t seem to make sense because surely a seperate object is created each time. Let’s check.
Nope, a
and b
are pointing to the same object. This seems to be an efficiency in Python where it tries not to create the same built in type twice.
Checking for None
At first it looks like x == None
works.
This is because None is an object in memory and id(None)
returns a memory address.
However, as we saw above, __eq__
can return anything and in particular it could always return True
, say. Here’s an example.
So when checking for None
using ==
carries a small risk and it’s safer to use is
.