Object Oriented Programming: Comparing and inheritance

Overloading equality

When comparing two objects of a custom class using ==, Python by default compares just the object references, not the data contained in the objects. To override this behavior, the class can implement the special __eq__() method, which accepts two arguments — the objects to be compared — and returns True or False. This method will be implicitly called when two objects are compared.

The BankAccount class from the previous chapter is available for you in the script pane. It has one attribute, balance, and a withdraw() method. Two bank accounts with the same balance are not necessarily the same account, but a bank account usually has an account number, and two accounts with the same account number should be considered the same.

Try selecting the code in lines 1-7 and pressing the “Run code” button. Then try to create a few BankAccount objects in the console and compare them.

  • Modify the __init__() method to accept a new parameter – number – and initialize a new number attribute.
  • Define an __eq__() method that returns True if the number attribute of two objects is equal.
  • Examine the print statements and the output in the console.
  • The __eq__() method should accept two arguments, usually called self and other.
  • When adding parameters to __init__(), remember that parameters without default values should be placed before parameters that have default values.
class BankAccount:
   # MODIFY to initialize a number attribute
    def __init__(self, number, balance=0):
        self.balance = balance
        self.number = number
      
    def withdraw(self, amount):
        self.balance -= amount 
    
    # Define __eq__ that returns True if the number attributes are equal 
    def __eq__(self, other):
        return self.number == other.number   

# Create accounts and compare them       
acct1 = BankAccount(123, 1000)
acct2 = BankAccount(123, 1000)
acct3 = BankAccount(456, 1000)
print(acct1 == acct2)
print(acct1 == acct3)
<script.py> output:
    True
    False

Notice that your method compares just the account numbers, but not balances. What would happen if two accounts have the same account number but different balances? The code you wrote will treat these accounts as equal, but it might be better to throw an error – an exception – instead, informing the user that something is wrong. At the end of the chapter, you’ll learn how to define your own exception classes to create these kinds of custom errors.

Checking class equality

In the previous exercise, you defined a BankAccount class with a numberattribute that was used for comparison. But if you were to compare a BankAccount object to an object of another class that also has a numberattribute, you could end up with unexpected results.

For example, consider two classes

class Phone: def __init__(self, number): self.number = number def __eq__(self, other): return self.number == \ other.number pn = Phone(873555333)class BankAccount: def __init__(self, number): self.number = number def __eq__(self, other): return self.number == \ other.number acct = BankAccount(873555333)

Running acct == pn will return True, even though we’re comparing a phone number with a bank account number. 

It is good practice to check the class of objects passed to the __eq__() method to make sure the comparison makes sense.

Both the Phone and the BankAccount classes have been defined. Try running the code as-is using the “Run code” button and examine the output.

  • Modify the definition of BankAccount to only return True if the numberattribute is the same and the type() of both objects passed to it is the same.

Run the code and examine the output again

class BankAccount:
    def __init__(self, number, balance=0):
        self.number, self.balance = number, balance
      
    def withdraw(self, amount):
        self.balance -= amount 

    # MODIFY to add a check for the type()
    def __eq__(self, other):
        return (self.number == other.number)

acct = BankAccount(873555333)
pn = Phone(873555333)
print(acct == pn)
OperatorMethod
==__eq__()
!=__ne__()
>=__ge__()
<=__le__()
>__gt__()
<__lt__()
  • __hash__() to use objects as dictionary keys and in sets
class BankAccount:
    def __init__(self, number, balance=0):
        self.number, self.balance = number, balance
      
    def withdraw(self, amount):
        self.balance -= amount 

    # MODIFY to add a check for the type()
    def __eq__(self, other):
        return ((self.number == other.number) & (type(self) == type(other)))

acct = BankAccount(873555333)
pn = Phone(873555333)
print(acct == pn)
<script.py> output:
    False

Now only comparing objects of the same class BankAccount could return True. Another way to ensure that an object has the same type as you expect is to use the isinstance(obj, Class) function. This can helpful when handling inheritance, as Python considers an object to be an instance of both the parent and the child class.

Try running pn == acct in the console (with reversed order of equality).

What does this tell you about the __eq__() method?

Comparison and inheritance

What happens when an object is compared to an object of a child class? Consider the following two classes:

class Parent:
    def __eq__(self, other):
        print("Parent's __eq__() called")
        return True

class Child(Parent):
    def __eq__(self, other):
        print("Child's __eq__() called")
        return True

The Child class inherits from the Parent class, and both implement the __eq__() method that includes a diagnostic printout.

Which __eq__() method will be called when the following code is run?

p = Parent()
c = Child()

p == c 

When ran in the console the result is:

Child's __eq__() called
True

This shows that when two classes, one as the parent class containing an __eq__() method, and the same for the child case. And when both are turned into an object and the objects are compared, then Python always calls the child’s __eq__()method when comparing a child object to a parent object.

Add a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.