# Inheritance, Method Overriding

**Inheritance enable us to define a class that takes all the functionality from parent class and allows us to add more. In this article, you will learn to use inheritance in Python.**

It refers to defining a new class with little or no modification to an existing class. The new class is called **derived (or child) class** and the one from which it inherits is called the **base (or parent) class**.

```python
class Polygon:
    def __init__(self, no_of_sides):
        self.n = no_of_sides
        self.sides = [4 for i in range(no_of_sides)]

    def inputSides(self):
        self.sides = [float(input("Enter side "+str(i+1)+" : ")) for i in range(self.n)]

    def dispSides(self):
        for i in range(self.n):
            print("Side",i+1,"is",self.sides[i])
            
class Triangle(Polygon):
    def __init__(self):
        #super().__init__(3)
        Polygon.__init__(self,3)

    def findArea(self):
        a, b, c = self.sides
        # calculate the semi-perimeter
        print(a,b,c)
        s = (a + b + c) / 2
        area = (s*(s-a)*(s-b)*(s-c)) ** 0.5
        print('The area of the triangle is %0.2f' %area)
        
t = Triangle()
t.inputSides()
t.dispSides()
t.findArea()
```

Enter side 1 : 5&#x20;

Enter side 2 : 6&#x20;

Enter side 3 : 5&#x20;

Side 1 is 5.0&#x20;

Side 2 is 6.0&#x20;

Side 3 is 5.0&#x20;

5.0 6.0 5.0&#x20;

The area of the triangle is 12.00

**Method Overriding**

In the above example, notice that `__init__()` method was defined in both classes, `Triangle` as well `Polygon`. When this happens, the method in the derived class overrides that in the base class. This is to say, `__init__()` in `Triangle` gets preference over the same in `Polygon`.

Generally when overriding a base method, we tend to extend the definition rather than simply replace it. The same is being done by calling the method in base class from the one in derived class (calling `Polygon.__init__()` from `__init__()` in `Triangle`).

A better option would be to use the built-in function `super()`. So, `super().__init__(3)` is equivalent to `Polygon.__init__(self,3)` and is preferred. You can learn more about the super() function in Python.

Two built-in functions `isinstance()` and `issubclass()` are used to check inheritances. Function `isinstance()` returns `True` if the object is an instance of the class or other classes derived from it. Each and every class in Python inherits from the base class `object`.

```python
>>> isinstance(t,Triangle)
True

>>> isinstance(t,Polygon)
True

>>> isinstance(t,object)
True
```

Issubclass() is used to check for class inheritance

```python
>>> issubclass(Polygon,Triangle)
False

>>> issubclass(Triangle,Polygon)
True
```

**Multiple Inheritance**

Like C++, a class can be derived from more than one base classes in Python. This is called multiple inheritance.

Example:

```python
class Base1:
    pass

class Base2:
    pass

class MultiDerived(Base1, Base2):
    pass
```

**Multilevel Inheritance**

On the other hand, we can also inherit form a derived class. This is called multilevel inheritance. It can be of any depth in Python.

In multilevel inheritance, features of the base class and the derived class is inherited into the new derived class.

```python
class Base:
    pass

class Derived1(Base):
    pass

class Derived2(Derived1):
    pass
```

Example - 1:

```python
# Parent class
class Dog:

    # Class attribute
    species = 'mammal'

    # Initializer / Instance attributes
    def __init__(self, name, age):
        self.name = name
        self.age = age

    # instance method
    def description(self):
        return "{} is {} years old".format(self.name, self.age)

    # instance method
    def speak(self, sound):
        return "{} says {}".format(self.name, sound)


# Child class (inherits from Dog class)
class RussellTerrier(Dog):
    def run(self, speed):
        return "{} runs {}".format(self.name, speed)


# Child class (inherits from Dog class)
class Bulldog(Dog):
    
    def __init__(self, n1, a1):
        self.n1 = n1
        self.a1 = a1
        print ("Dog name is {} and age is {}".format(self.n1,self.a1))
    
    def run(self, speed):
        return "{} runs {}".format(self.n1, speed)


# Child classes inherit attributes and
# behaviors from the parent class
jim = Bulldog("Jim", 12)
#print(jim.description())

# Child classes have specific attributes
# and behaviors as well
print(jim.run("slowly"))
```

Dog name is Jim and age is 12&#x20;

Jim runs slowly

Example -2:

```python
# Parent class
class Dog:

    # Class attribute
    species = 'mammal'

    # Initializer / Instance attributes
    def __init__(self, name, age):
        self.name = name
        self.age = age

    # instance method
    def description(self):
        return "{} is {} years old".format(self.name, self.age)

    # instance method
    def speak(self, sound):
        return "{} says {}".format(self.name, sound)


# Child class (inherits from Dog() class)
class RussellTerrier(Dog):
    def run(self, speed):
        return "{} runs {}".format(self.name, speed)


# Child class (inherits from Dog() class)
class Bulldog(Dog):
    def run(self, speed):
        return "{} runs {}".format(self.name, speed)


# Child classes inherit attributes and
# behaviors from the parent class
jim = Bulldog("Jim", 12)
print(jim.description())

# Child classes have specific attributes
# and behaviors as well
print(jim.run("slowly"))

# Is jim an instance of Dog()?
print(isinstance(jim, Dog))

# Is julie an instance of Dog()?
julie = Dog("Julie", 100)
print(isinstance(julie, Dog))

# Is johnny walker an instance of Bulldog()
johnnywalker = RussellTerrier("Johnny Walker", 4)
print(isinstance(johnnywalker, Bulldog))

# Is julie and instance of jim?
print(isinstance(julie, jim))
```

Jim is 12 years old&#x20;

Jim runs slowly&#x20;

True&#x20;

True

False

TypeError                       Traceback (most recent call last)

\<ipython-input-69-c976ae4d1a2c> in \<module>

&#x20;52&#x20;

53 # Is julie and instance of jim?&#x20;

\---> 54 print(isinstance(julie, jim))

TypeError: isinstance() arg 2 must be a type or tuple of types

Example - 3 :

```python
class Dog:
    species = 'mammal'

class SomeBreed(Dog):
    pass

class SomeOtherBreed(Dog):
    species = 'reptile'

frank = SomeBreed()
print(frank.species)

beans = SomeOtherBreed()
print(beans.species)
print(frank.species)
```

mammal

reptile

mammal

### Method Resolution Order in Python

Every class in Python is derived from the class `object`. It is the most base type in Python.

So technically, all other class, either built-in (int, string,list,etc) or user-defines, are derived classes and all objects are instances of `object` class.

```python
print(issubclass(list,object))
print(issubclass(int, object))
# Output: True
print(isinstance(5.5,object))

# Output: True
print(isinstance("Hello",object))
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://gyansetu-python.gitbook.io/python-programming/oops/inheritance-1.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
