Checking Equivalence Between Objects
00:00 In this lesson, you’re going to find out how to check equivalence between objects. Objects behave slightly differently from normal values in that you can’t compare them the same way you would, say, a number.
00:10
There is a built-in function called isinstance()
, which is the recommended way to check between objects to check if they’re the same type, but you can also make use of the built-in type()
function as well.
00:24
So say you had two points, origin
and target
, and say that they were actually equivalent in the sense that you and I know from looking at these points that they represent the same point in space, the 0, 0
origin point.
00:38
So in many ways, they can be considered the same or equal. However, if you try and just do a direct compare (==
), you’ll see that they’re False
.
00:47
And this is true also of is
. This is because objects or instances behave very differently. They actually live at different memory addresses, as you’ve seen at the start of the course. They are separate objects.
01:00
Even though they have the same instance attributes doesn’t mean they are the same or can be compared straight away. There are some things that you can check right off the bat using isinstance()
.
01:12
If you call isinstance()
, which is a inbuilt function—you don’t need to import it or anything—the first argument is the object that you want to test, and you want to check if it’s an instance of the second argument, a Point
.
01:26
So in this case, you are checking whether origin
, is that an instance of Point
? And that is True
. Likewise, target
is also an instance of Point
.
01:38
You can also check that both are the same type by checking type(origin) == type(target)
. These will produce the same output, so you can check that they are the same type.
01:53 To copy the example that you’ve just seen, you’ve got two points loaded up, and you can’t just compare them directly.
02:00
That will give you a False
value because these are different objects. They’ve been both instantiated individually, so they are different. So you can’t just compare them directly.
02:10
There are ways to override how objects behave when in between the ==
(equals to) operator and also the >
(greater than) and <
(less than).
02:18
You can define all of these behaviors individually, but that’s going to come in a later lesson with something called special methods, or dunder methods. You do have a couple tools right off the bat, which is isinstance()
,
02:33
and that will check if, say, like target
, I want to know if target
is a member of the Point
class. That’s True
.
02:41
And you can also check what the type of target
is, and this will give you this notation here that says it is a member of the Point
class. But as you can see, this type of output isn’t maybe as clear as just checking isinstance()
, where you can put the name of the class, and it’s much easier to work with. If you wanted to check whether origin
and target
had the same values and so that they represented the same point, without dunder methods, what you’d have to do for now is to go origin.x == target.x and origin.y == target.y
, and that will give you True
because they both have 0, 0
as their point.
03:28 So this more verbose way is the way you’d have to do it for now. And that’s just to say you can’t compare classes directly without doing something special first.
Bartosz Zaczyński RP Team on Jan. 27, 2023
@DimaG With a regular class, you have ultimate control over the comparison logic between its instances. By default, Python compares objects of your class by their identity, which is their memory address in CPython. Take a look at this example:
>>> class RegularClass:
... pass
>>> instance1 = RegularClass()
>>> instance2 = RegularClass()
>>> instance1 == instance2
False
The two objects compare unequal because they are two separate objects, even if they happen to represent the same value conceptually. To change that, for example, by tying the result of the equality test to values stored in your objects, you can override the equality test operator (==
) by implementing the special method .__eq__()
in your class:
>>> class RegularClass:
... def __init__(self, value):
... self.value = value
...
... def __eq__(self, other):
... if other is self:
... return True
... if type(self) is not type(other):
... return False
... return self.value == other.value
>>> instance1 = RegularClass(42)
>>> instance2 = RegularClass(42)
>>> instance3 = RegularClass(555)
>>> instance1 == instance2
True
>>> instance1 == instance3
False
Now, the objects of your class behave differently. The two instances that represent the same value, 42, compare equal. However, the third instance is considered unequal to the other two because, although it has the same type, it contains a different value.
Side note: If you intend to use objects of your class as dictionary keys or set members, then you should also provide the corresponding implementation of the special method .__hash__()
to follow the hash-equal contract.
When you decorate your class with the @dataclass
decorator, Python generates a number of special methods, including the .__eq__()
and .__hash__()
for you. Because data classes represent data, their comparison is assumed to work like in the second example above, where the attribute values of your data class determine equality:
>>> from dataclasses import dataclass
>>> @dataclass
... class DataClass:
... value: int
...
>>> DataClass(42) == DataClass(42)
True
>>> DataClass(42) == DataClass(555)
False
There’s nothing special about data classes. They’re just syntactic sugar that does some work for you under the surface.
Become a Member to join the conversation.
DimaG on Jan. 26, 2023
I have a question. If I check class instances for equality (
==
) it returnsTrue
when class is declared using dataclass class syntax andFalse
if a class is declared with regular class syntax. Why is that? I had read the documentation on dataclasses, but it does not say anything about storing class instances any differently than class that is using regular class syntax. The difference in output applies only to equality comparison, every other comparison is the same between two classes. I use Python 3.11 if it matters.