Help support the author by donating or purchasing a PDF copy of the book from EJunkie!

Buy me a coffeeBuy me a coffee

Chapter 18 - OOP: Special Methods and Operator Overloading

18.1 - The __init__() method

In the previous chapter we looked at how to define new types by creating classes. We also had to initialise our instance objects using methods like initialise(). We don’t have to do this with Python’s built-in types such as strings.

For example, to initialise a list we could just type my_list = [1, 2, 3].

We can define a special method called __init__() (that’s double underscore). This is called a constructor. If we provide that method for our class, then it is automatically called when we create an object. We can therefore replace any initialisation methods we had previously defined with __init__(). This allows us to create and initialise our object in one step.

Let’s look at a Point that models a point.

We can now use our point class as follows:

As you can see, our __init__() method was called automatically when we created an instance of the point class.

If the __init__() method takes two arguments (excluding self), then we must pass two arguments, otherwise we’ll get an error.

We can however, set default values for these arguments. This will allow us to initialise an instance of Point without passing the arguments:

We can now use our class as follows:

What happens when we define this special method and create an instance is:

Remember, special methods are never called directly!

18.2 - The __str__() method

The __str__() method is another special function. We can override it which means we can change it’s implementation and hence it’s behaviour.

When we call print() on a Python built-in such as a string or a list, we get a nicely formatted, human readable representation of the object.

Under the hood, the list built in type implements this __str__() method and hence, allows us to print our lists in human readable format.

We can override this method to allow us to format and print our objects as we see fit. Therefore, when we call print() on an object of a class we’ve defined, we’ll no longer get something like <time.Time object at 0x000001A7C6902F60> printed to the terminal.

We do this by returning a formatted string. Let’s look at how we might do this for the Pointclass. We could now replace our display() function and replace it with __str__(), that way we can just print our point instance.

We can now use our class as follows:

The return value of this special method must be a string object.

18.3 - The __len__() method

As you can probably guess, the __len__() method allows us override the default implementation of the len() function to define what the length of our own types are. For example with a Point, the length may be the distance from the origin ( the point (0, 0)). However, we must return an integer. We will get an error otherwise. So, we’ll have to round either up or down. Python does this in order to make the len() function predictable. In this example, we’ll just cast our return value to an integer:

We can now call the len() function on instances of our Point class.

The actual distance of this point from the center is 40.8044…

It is unfortunate that we can’t return a floating point number, therefore if we want real accuracy we’ll have to implement a different method that we’ll have to call on the instance.

18.4 - Operator overloading

There are a tonne of special methods in Python, I’m not going to cover them all, however, there are some special methods that are called when we use operators. For example, when we use the comparison operator == on two integers, a special method called __eq__() is called.

When method overriding is done on methods that define the behaviour of operators, we call that: operator overloading.

We can overload the default behaviour of the == method, so that it applies to our Point class. This is done as follows:

In this case, we overload the __eq__() method by comparing two tuples. If the two tuples, each containing the x and y coordinates of points, are equal, then our two points are equal. We can use this as follows:

We could also add two points together to get a new point. In this case we would overload the __add()__ method. The __add__() method implements the functionality of the + operator, not the += operator. Therefore, we must return a new object as the + operator is not for in-place addition.

Let’s look at that:

Now we can use this method as follows:

We can overload the += operator, which is for in-place addition. This way we don’t need to return a reference to a new point object and assign that to a new variable p3, instead we update the object’s (p1 in this case) data attributes.

This is done by overloading the __iadd()__ method.

We can now use the += operator on our points

Be careful when overloading in-place operators. In this case we use create a temporary point and then update the self instance using multiple assignment. Remember when we swapped the value in one variable for another and we had to create a temp variable? We can do that much faster by doing x, y = y, x. In this case x, y and y, x are tuples!

Anyway, after we update the attributes for self, we must return self.

Below are a list of other operators you can overload.

Operator Method that can be overloaded
== __eq__()
!= __ne__()
< __lt__()
<= __le__()
> __gt__()
>= __ge__()
+ __add__()
- __sub__()
// __floordiv__()
/ __truediv__()
* __mul__()
** __pow__()
% __mod__()
+= __iadd__()
-= __isub__()
*= __imul__()
//= __ifloordiv__()
/= __itruediv__()
**= __ipow__()

There are more but we won’t need any more than this, in fact we won’t use half of these in this book.

18.5 - Exercises

Question 1

Create a BankAccount class. The bank account class should have the following attributes: balance and account_owner. The bank account class should also have deposit and withdraw methods. You should also be able to print the bank account, to display the account owners name and balance. When implemented correctly, you should be able to use the Bank account class as follows:

Question 2

Create a Line class. The line class should have four attributes: an x1 and a y1 coordinate for one point on the line and an x2 and a y2 coordinate for the second point on the line. You should be able to do a few things with an instance of a line. You should be able to call the len method on a line to show its length, add two lines together, subtract two lines, multiply a line by an integer, print the line and compare two lines to see if they are equal. In this case equality means both lines are the same length. When implemented correctly, you should be able to use the Line class as follows:

Note: The length of the line must return an integer.

Help support the author by donating or purchasing a PDF copy of the book from EJunkie!

Buy me a coffeeBuy me a coffee

Previous Chapter - Next Chapter