Using Python’s Magic Methods to compare Tesla and Ford
Tags: python
Published: 2020-08-15 19:58:34

Magic Methods are the special methods which gives us the ability to access built in syntactical features such as ‘<’, ‘>’, ‘==’, ‘+’ etc..

You must have worked with such methods without knowing them to be as magic methods. Magic methods can be identified with their names which start with __ and ends with __ like `__init__`, `__call__`, `__str__` etc. These methods are also called Dunder Methods, because of their name starting and ending with Double Underscore (Dunder).

Now there are a number of such special methods, which you might have come across too, in Python. We will just be taking an example of a few of them to understand how they work and how we can use them.
##1. `__init__`
```
class AnyClass:
    def __init__():
        print("Init called on its own")
obj = AnyClass()
```
The first example is __init__, and as the name suggests, it is used for initializing objects. Init method is called on its own, ie. whenever an object is created for the class, the __init__ method is called on its own.

The output of the above code will be given below. Note how we did not call the init method and it got invoked as we created an object for class AnyClass.
Init called on its own

##2. `__add__`
Let’s move to some other example, __add__ gives us the ability to access the built in syntax feature of the character +. Let’s see how,
```
class AnyClass:
    def __init__(self, var):
        self.some_var = var
    def __add__(self, other_obj):
        print("Calling the add method")
        return self.some_var + other_obj.some_var
obj1 = AnyClass(5)
obj2 = AnyClass(6)
obj1 + obj2
```
As we use the + character, it will call the __add__ method. Now observe the __add__, it expects the second argument to be other_obj, and hence we have the access to add not just some_var of the other object but any other variable as well. The output would be,
```
"Calling the add method"
11
```

Similar to __add__, we can have
1. `__eq__`(self, other_obj)Defines behavior for the equality operator, ==.
2. `__ne__`(self, other_obj)Defines behavior for the inequality operator, !=.
3. `__lt__`(self, other_obj)Defines behavior for the less-than operator, <.
4. `__gt__`(self, other_obj)Defines behavior for the greater-than operator, >.
5. `__le__`(self, other_obj)Defines behavior for the less-than-or-equal-to operator, <=.
6. `__ge__`(self, other_obj)Defines behavior for the greater-than-or-equal-to operator, >=.

##3. `__repr__`
It is used to represent the object. It gives all the representation of all the information of the object. We will understand more about it in the example later.
If we create an object of the class AnyClass from the previous example, and do repr(obj) or print(obj) it will return
`'<__main__.AnyClass object at 0x0000025B8FB75048>'`
Let’s add the __repr__ method to our AnyClass, and see the ‘magic’.
```
class AnyClass:
    def __init__(self, var):
        self.some_var = var
def __repr__(self):
        print(f"AnyClass with some_var value as {self.some_var}")
obj = AnyClass(5)
print(obj)
```
The output of the above example will be,
`AnyClass with some_var value as 5`

Now, let’s use these magic methods and compare Tesla Model 3 and Ford Mustang

Lets create a class Car and define these magic methods to get the ability to use the operators and compare the Car’s attributes such as Top Speed, Horsepower and Price.
```
class Car:
    def __init__(self, name, manufacturer, horsepower, top_speed, cost, length):
        self.name = name
        self.manufacturer = manufacturer
        self.horsepower = horsepower
        self.top_speed = top_speed
        self.cost = cost
        self.length = length
    def __sub__(self, other):
        print(f"Difference In Price: {self.cost - other.cost}")
        print(f"Difference in Horsepower: {self.horsepower -     other.horsepower}")
        print(f"Difference in Top Speed: {self.top_speed -   other.top_speed}")
    def __len__(self):
        return f"{self.length}m"
```
If we run this program and command,
```
tesla = Car(name='Model 3', manufacturer='Tesla', horsepower=283, top_speed=129, cost=35000, length=4.69)
ford = Car(name='Mustang Mach E', manufacturer='Ford', horsepower=282, top_speed=132, cost=25100, length=4.72)
ford - tesla
len(ford)
len(tesla)
```
Will return us,
`Difference In Price: $8895`
`Difference in Horsepower: -1`
`Difference in Top Speed: 3`
`4.72m`
`4.69m`

The ‘-’ operator gave us the difference in price, power and speed, it’s nothing less of a ‘magic’ if you don’t know how it works. But now we do know, and lets implement other methods. And len gave us the lengths of the cars ie. the length attribute of the Car object.

{% gist https://gist.github.com/shivambats/c523fd34f9b3b4d45c5fea0050d1de0f %}

By this program, we will be able to run commands such as,`tesla == ford
tesla != ford, tesla < ford` etc..

Note this might not be something that you will have to do in real life but this is one of the examples to showcase how we can make use of the built in features of Python, and understand the concepts behind them.