Python __eq__
Summary: in this tutorial, you’ll learn how to use the Python __eq__ method to compare two objects by their values.
Introduction to the Python __eq__ method
Suppose that you have the following Person class with three instance attributes: first_name , last_name , and age :
class Person: def __init__(self, first_name, last_name, age): self.first_name = first_name self.last_name = last_name self.age = age
Code language: Python (python)
And you create two instances of the Person class:
john = Person('John', 'Doe', 25) jane = Person('Jane', 'Doe', 25)
Code language: Python (python)
In this example, the john and jane objects are not the same object. And you can check it using the is operator:
print(john is jane) # False
Code language: Python (python)
Also, when you compare john with jane using the equal operator (==), you’ll get the result of False:
print(john == jane) # False
Code language: Python (python)
Since john and jane have the same age, you want them to be equal. In other words, you want the following expression to return True :
To do it, you can implement the __eq__ dunder method in the Person class.
Python automatically calls the __eq__ method of a class when you use the == operator to compare the instances of the class. By default, Python uses the is operator if you don’t provide a specific implementation for the __eq__ method.
The following shows how to implement the __eq__ method in the Person class that returns True if two person objects have the same age:
class Person: def __init__(self, first_name, last_name, age): self.first_name = first_name self.last_name = last_name self.age = age def __eq__(self, other): return self.age == other.age
Code language: Python (python)
Now, if you compare two instances of the Person class with the same age, it returns True:
john = Person('John', 'Doe', 25) jane = Person('Jane', 'Doe', 25) print(john == jane) # True
Code language: Python (python)
And if two instances of the Person class don’t have the same age, the == operator returns False:
john = Person('John', 'Doe', 25) mary = Person('Mary', 'Doe', 27) print(john == mary) # False
Code language: Python (python)
The following compares a Person object with an integer:
john = Person('John', 'Doe', 25) print(john == 20)
Code language: PHP (php)
AttributeError: 'int' object has no attribute 'age'
Code language: JavaScript (javascript)
To fix this, you can modify the __eq__ method to check if the object is an instance of the Person class before accessing the age attribute.
If the other object isn’t an instance of the Person class, the __eq__ method returns False , like this:
class Person: def __init__(self, first_name, last_name, age): self.first_name = first_name self.last_name = last_name self.age = age def __eq__(self, other): if isinstance(other, Person): return self.age == other.age return False
Code language: Python (python)
And you can now compare an instance of the Person class with an integer or any object of a different type:
john = Person('John', 'Doe', 25) print(john == 20) # False
Code language: Python (python)
class Person: def __init__(self, first_name, last_name, age): self.first_name = first_name self.last_name = last_name self.age = age def __eq__(self, other): if isinstance(other, Person): return self.age == other.age return False john = Person('John', 'Doe', 25) jane = Person('Jane', 'Doe', 25) mary = Person('Mary', 'Doe', 27) print(john == jane) # True print(john == mary) # False john = Person('John', 'Doe', 25) print(john == 20) # False
Code language: Python (python)
Summary
- Implement the Python __eq__ method to define the equality logic for comparing two objects using the equal operator ( == )
Python Class Equality
- Equality of Class Objects in Python
- Python Class Equality Using the __eq__() Method
- Python Class Equality Using the id() Method
In Python, we can compare different data types using comparison operators. However, we cannot simply compare them using the comparison operators when creating custom classes.
This article will discuss different ways to check the equality of objects defined using custom classes in Python.
Equality of Class Objects in Python
When we have built-in objects like integers or strings, we can easily check for their equality using the == operator, as shown below.
num1 = 12 num2 = 10 result = num1 == num2 print("<> and <> are equal:<>".format(num1, num2, result))
Here, the == operator gives the correct value as output because the values 12 and 10 are integers. However, when we have objects of custom classes, the Python interpreter works differently.
For instance, suppose that we have a Length class with only one attribute, length , as shown below.
class Length: def __init__(self, value): self.length = value
We will create two instances of the class Length with the same value in the length attribute.
class Length: def __init__(self, value): self.length = value len1 = Length(10) len2 = Length(10)
If you compare the objects using the == operator, the result will be False even though both the instances have the same value in the length attribute. You can observe this in the following code.
class Length: def __init__(self, value): self.length = value len1 = Length(10) len2 = Length(10) result = len1 == len2 print("len1 and len2 are equal:", result)
len1 and len2 are equal: False
The above behavior of the Python interpreter can be described using the way it compares two objects of user-defined classes. When we check the equality of two class objects in Python using the == operator, the result will be True only if both the objects refer to the same memory location.
In other words, there will be two variables but only a single Python object. You can observe this in the following example.
class Length: def __init__(self, value): self.length = value len1 = Length(10) len2 = len1 result = len1 == len2 print("len1 and len2 are equal:", result)
len1 and len2 are equal: True
You might have understood that the equality operator will return True only when both the variables refer to the same instance of the user-defined class.
What should we do if we need to check the equality of different instances of a class in Python? Let us find out.
Python Class Equality Using the __eq__() Method
By overriding the __eq__() method, we can modify how the == operator works with custom classes. For instance, to check the length of two instances of the Length class, we can override the __eq__() method.
The __eq__() method, when invoked on an instance of the Length class, will take another object as its input argument.
Inside the __eq__() method, we will first check if the input object is an instance of the Length class or not. For this, we can use the isinstance() function.
The isinstance() function takes a Python object as its first input argument and the class name as its second input argument. After execution, it returns True if the object is an instance of the class provided in the input argument.
We will pass the Length class as the second input argument in our program. If the object passed in the first argument is not an instance of the Length class, it will return False .
Otherwise, we will proceed ahead.
To check for class equality of the two objects, we will compare the attribute length value in both objects. If the values are equal, we will return True .
Once the __eq__() method is implemented in the Length class, we can correctly compare two instances of the Number class using the == operator.
Suppose we have two instances of the Length class, say len1 and len2 . When we perform len1==len2 , the len1.__eq__(len2) method will be executed.
Similarly, when we perform len2==len1 , the len2.__eq__(len1) method will be executed.
After executing the code, len1==len2 will return True if both objects’ length value has the same value. Otherwise, it will return False .
You can observe this in the following example.
class Length: def __init__(self, value): self.length = value def __eq__(self, other): isLength = isinstance(other, self.__class__) if not isLength: return False if self.length == other.length: return True else: return False len1 = Length(10) len2 = Length(10) result = len1 == len2 print("len1 and len2 are equal:", result)
len1 and len2 are equal: True
Python Class Equality Using the id() Method
You can also check if two variables having objects of custom classes refer to the same object or not. For this, you can use the id() function.
The id() function takes an object as its input argument and returns a unique identity number at any memory location. You can observe this in the following example.
class Length: def __init__(self, value): self.length = value def __eq__(self, other): isLength = isinstance(other, self.__class__) if not isLength: return False if self.length == other.length: return True else: return False len1 = Length(10) len2 = Length(10) result1 = id(len1) result2 = id(len2) print("ID of len1 is ", result1) print("ID of len2 is ", result2)
ID of len1 is 140057455513712 ID of len2 is 140057454483488
If two objects refer to the same memory location, the id() function will give the same output for both objects. By comparing the output of the id() function, we can check if the objects refer to the same memory location or not.
You can observe this in the following example.
class Length: def __init__(self, value): self.length = value def __eq__(self, other): isLength = isinstance(other, self.__class__) if not isLength: return False if self.length == other.length: return True else: return False len1 = Length(10) len2 = Length(10) result1 = id(len1) result2 = id(len2) result = result1 == result2 print("len1 and len2 are equal:", result)
len1 and len2 are equal: False
Here, you can observe that we haven’t checked the value of the attributes in the objects to check for class equality.
In this case, we are only checking if the objects refer to the same memory location or not. Thus, this approach to checking Python class equality is equivalent to using the == operator without implementing the __eq__() method in the class definition.
Related Article — Python Class
Copyright © 2023. All right reserved
Методы сравнений __eq__, __ne__, __lt__, __gt__ и другие
Но здесь объекты сравниваются по их id (адресу в памяти), а мы бы хотели, чтобы сравнивались секунды в каждом из объектов c1 и c2. Для этого переопределим магический метод __eq__(), следующим образом:
def __eq__(self, other): if not isinstance(other, (int, Clock)): raise TypeError("Операнд справа должен иметь тип int или Clock") sc = other if isinstance(other, int) else other.seconds return self.seconds == sc
Теперь, после запуска программы видим значение True, т.к. объекты содержат одинаковое время. Кроме того, мы можем совершенно спокойно выполнять проверку и на неравенство:
Смотрите, если интерпретатор языка Python не находит определение метода ==, то он пытается выполнить противоположное сравнение с последующей инверсией результата. То есть, в данном случае находится оператор == и выполняется инверсия: not (a == b) Давайте в этом убедимся, поставим точку останова в метод __eq__ и запустим программу. Как видите, он срабатывает и результат в последствии меняется на противоположный. Отлично, на равенство и неравенство мы теперь можем сравнивать объекты класса Clock, а также с целыми числами. Однако, сравнение на больше или меньше пока не работает. Строчка программы:
def __lt__(self, other): if not isinstance(other, (int, Clock)): raise TypeError("Операнд справа должен иметь тип int или Clock") sc = other if isinstance(other, int) else other.seconds return self.seconds sc
Как видите, у нас здесь получается дублирование кода. Поэтому, я вынесу общее для методов сравнения в отдельный метода класса:
@classmethod def __verify_data(cls, other): if not isinstance(other, (int, Clock)): raise TypeError("Операнд справа должен иметь тип int или Clock") return other if isinstance(other, int) else other.seconds
def __eq__(self, other): sc = self.__verify_data(other) return self.seconds == sc def __lt__(self, other): sc = self.__verify_data(other) return self.seconds sc
Итак, мы определили сравнение на равенство и меньше. Теперь, можно сравнивать объекты класса Clock на эти операции и дополнительно на неравенство и больше. Сейчас команда:
c1 = Clock(1000) c2 = Clock(2000) print(c1 c2)
Выдаст True, так как первое время меньше второго. И также мы можем совершенно спокойно делать проверку на больше:
Здесь сработает тот же метод меньше, но для объекта c2: c2 < c1 То есть, в отличие от оператора ==, где применяется инверсия, здесь меняется порядок операндов. Разумеется, если в классе определен метод больше:
def __gt__(self, other): sc = self.__verify_data(other) return self.seconds > sc
то он будет найден и выполнен. Подмена происходит только в случае отсутствия соответствующего магического метода. И то же самое для методов сравнения на меньше или равно и больше или равно:
def __le__(self, other): sc = self.__verify_data(other) return self.seconds sc