Skip to content

Added 22-inheritance.md #22

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 15, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
345 changes: 345 additions & 0 deletions 22-inheritance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,345 @@
# Python Inheritance

**Video link:** [https://youtu.be/C8qE3mKiBrQ](https://youtu.be/C8qE3mKiBrQ)

In this video, you learned about inheritance in Python with the help of examples. We also covered the concepts of method overriding and the `super()` function.

**Programs in the Video**

- [Inheritance in Python](#inheritance-in-python)
- [Example of Inheritance](#example-of-inheritance)
- [Method Overriding](#method-overriding)
- [The `super()` function](#the-super-function)

---

## Inheritance in Python
Let's derive a dog and cat class from an animal class and get a feel of how inheritance works.

Suppose we have an `Animal` class:

```python
class Animal:
def eat(self):
print("I can eat")
```

Now, let's derive a `Dog` class from `Animal`:

```python
class Animal:
def eat():
print("I can eat")

class Dog(Animal):
def bark(self):
print("I can bark")
```

Objects of `Dog` can access methods and attributes of both `Dog` and `Animal`:


```python
class Animal:
def eat():
print("I can eat")

class Dog(Animal):
def bark(self):
print("I can bark")

dog1 = Dog()

dog1.bark()
dog1.eat()
```

**Output**
```
I can bark
I can eat
```

Similarly, if we want, we can derive another `Cat` class from the `Animal` class:


```python
class Animal:
def eat():
print("I can eat")

class Dog(Animal):
def bark(self):
print("I can bark")

class Cat(Animal):
def get_grumpy(self):
print("I am getting grumpy.")

dog1 = Dog()

dog1.bark()
dog1.eat()

cat1 = Cat()
cat1.eat()
```

**Output**

```
I can bark
I can eat
I can eat
```

---

## Example of Inheritance

Let's implement a program to calculate the perimeter of different polygons like triangles and quadrilaterals using inheritance.

Let's first create a base class called `Polygon`:

```python
class Polygon:
def __init__(self, sides):
self.sides = sides

def display_info(self):
print("A polygon is a two dimensional shape with straight lines")

def get_perimeter(self):
perimeter = sum(self.sides)
return perimeter
```

All polygons like triangles and quadrilaterals will derive these features.

Let's create a `Triangle` class that will inherit from the `Polygon` class:


```python
class Polygon:
def __init__(self, sides):
self.sides = sides

def display_info(self):
print("A polygon is a two dimensional shape with straight lines")

def get_perimeter(self):
perimeter = sum(self.sides)
return perimeter


class Triangle(Polygon):
def display_info(self):
print("A triangle is a polygon with 3 edges")
```

I have redefined the `display_info()` method to display information specific to triangles.

Similarly, I will also define a new `Quadrilateral` class.

```python
class Polygon:
def __init__(self, sides):
self.sides = sides

def display_info(self):
print("A polygon is a two dimensional shape with straight lines")

def get_perimeter(self):
perimeter = sum(self.sides)
return perimeter


class Triangle(Polygon):
def display_info(self):
print("A triangle is a polygon with 3 edges")


class Quadrilateral(Polygon):
def display_info(self):
print("A quadrilateral is a polygon with 4 edges")
```

Now, let's find the perimeter of a triangle. I will create an object from `Triangle` and use its `get_perimeter()` method.


```python
class Polygon:
def __init__(self, sides):
self.sides = sides

def display_info(self):
print("A polygon is a two dimensional shape with straight lines")

def get_perimeter(self):
value = 0
for side in self.sides:
value += side
return value


class Triangle(Polygon):
def display_info(self):
print("A triangle is a polygon with 3 edges")


class Quadrilateral(Polygon):
def display_info(self):
print("A quadrilateral is a polygon with 4 edges")

t1 = Triangle([5, 6, 7])
perimeter = t1.get_perimeter()
print("Perimeter:", perimeter)
```

**Output**

```
Perimeter: 18
```

---

## Method Overriding

If you have noticed, in the above example, we have the `display_info()` method in both our base class and the derived classes.

Let's see what will happen if we call the `display_info()` method for the `t1` triangle.

```python
class Polygon:
def __init__(self, sides):
self.sides = sides

def display_info(self):
print("A polygon is a two dimensional shape with straight lines")

def get_perimeter(self):
perimeter = sum(self.sides)
return perimeter


class Triangle(Polygon):
def display_info(self):
print("A triangle is a polygon with 3 edges")


class Quadrilateral(Polygon):
def display_info(self):
print("A quadrilateral is a polygon with 4 edges")

t1 = Triangle([5, 6, 7])
perimeter = t1.get_perimeter()
print("Perimeter:", perimeter)

t1.display_info()
```

**Output**

```
Perimeter: 18
A triangle is a polygon with 3 edges
```

We can see that the `display_info()` method of the `Triangle` class is called and `display_info()` of its parent class is not executed.

This is called method overriding.

**If the same method is defined in both the base and the derived class, then the method of the derived class overrides the method of the base class.**


If we need, we can call the `display_info()` method of our parent `Polygon` class from inside its child classes like this:

```python
class Polygon:
def __init__(self, sides):
self.sides = sides

def display_info(self):
print("A polygon is a two dimensional shape with straight lines")

def get_perimeter(self):
perimeter = sum(self.sides)
return perimeter


class Triangle(Polygon):
def display_info(self):
print("A triangle is a polygon with 3 edges")
Polygon.display_info(self)


class Quadrilateral(Polygon):
def display_info(self):
print("A quadrilateral is a polygon with 4 edges")

t1 = Triangle([5, 6, 7])
perimeter = t1.get_perimeter()
print("Perimeter:", perimeter)

t1.display_info()
```

**Output**
```
Parameter: 18
A triangle is a polygon with 3 edges
A polygon is a two dimensional shape with straight lines
```

>**Note:** `Polygon` is the name of the parent class. Since we are calling the method using a class rather than an object, we also need to pass the `self` object manually.

This code is a bit more unorthodox than what we have been using. There is a more elegant way to achieve the same task by using the `super()` function.

---

## The `super()` function

The `super()` function returns a temporary object of the superclass for a subclass.

```python
class Polygon:
def __init__(self, sides):
self.sides = sides

def display_info(self):
print("A polygon is a two dimensional shape with straight lines")

def get_perimeter(self):
value = 0
for side in self.sides:
value += side
return value


class Triangle(Polygon):
def display_info(self):
print("A triangle is a polygon with 3 edges")
# Polygon.display_info(self)
super().display_info()

class Quadrilateral(Polygon):
def display_info(self):
print("A quadrilateral is a polygon with 4 edges")

t1 = Triangle([5, 6, 7])
perimeter = t1.get_perimeter()
print("Perimeter:", perimeter)

t1.display_info()
```

**Output**

```
Perimeter: 18
A triangle is a polygon with 3 edges
A polygon is a two dimensional shape with straight lines
```

In this case, `super()` is an object of `Polygon`. We are using it to call `display_info()` of the `Polygon` class.