{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "All the IPython Notebooks in **Python Object Class** lecture series by **[Dr. Milaan Parmar](https://www.linkedin.com/in/milaanparmar/)** are available @ **[GitHub](https://github.com/milaan9/06_Python_Object_Class)**\n", "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Python Inheritance\n", "\n", "The **process of inheriting the properties of the base (or parent) class into a derived (or child) class is called inheritance**. Inheritance enables us to define a class that takes all the functionality from a base class and allows us to add more. In this tutorial, you will learn to use inheritance in Python." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Inheritance in Python\n", "\n", "Inheritance is a powerful feature in object oriented programming. The main purpose of inheritance is the reusability of code because we can use the existing class to create a new class instead of creating it from scratch.\n", "\n", "It refers to defining a new [**class**](https://github.com/milaan9/06_Python_Object_Class/blob/main/002_Python_Classes_and_Objects.ipynb) 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.\n", "\n", "In inheritance, the derived class acquires and access all the data members, properties, and functions from the base class. Also, a derived class can also provide its specific implementation to the methods of the base class.\n", "\n", "
\n", "\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Syntax:**\n", "\n", "```python\n", "class BaseClass:\n", " Body of base class\n", "class DerivedClass(BaseClass):\n", " Body of derived class\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Types Of Inheritance\n", "\n", "In Python, based upon the number of child and parent classes involved, there are five types of inheritance. The type of inheritance are listed below:\n", "\n", "1. Single Inheritance\n", "2. Multiple Inheritance\n", "3. Multilevel inheritance\n", "4. Hierarchical Inheritance\n", "5. Hybrid Inheritance\n", "\n", "Now let’s see each in detail with example." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Python Single Inheritance\n", "\n", "In single inheritance, a derived class inherits from a single-base class. Here in one derived class and one base class.\n", "\n", "
\n", "\n", "
\n", "\n", "Here, the **`Derived`** class is derived from **`Base`** class." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "ExecuteTime": { "end_time": "2021-06-20T19:10:28.178811Z", "start_time": "2021-06-20T19:10:28.165139Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Inside Vehicle class\n", "Inside Car class\n" ] } ], "source": [ "# Example 1: Single Inheritance\n", "\n", "# Base class\n", "class Vehicle:\n", " def Vehicle_info(self):\n", " print('Inside Vehicle class')\n", "\n", "# Derived class\n", "class Car(Vehicle):\n", " def car_info(self):\n", " print('Inside Car class')\n", "\n", "# Create object of Car\n", "car = Car()\n", "\n", "# access Vehicle's info using car object\n", "car.Vehicle_info()\n", "car.car_info()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Python Multiple Inheritance\n", "\n", "In multiple inheritance, one derived class can inherit from multiple base classes. \n", "\n", "
\n", "\n", "
\n", "\n", "Here, the **`MultiDerived`** class is derived from **`Base1`** and **`Base2`** classes." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "ExecuteTime": { "end_time": "2021-06-20T19:10:31.094801Z", "start_time": "2021-06-20T19:10:31.069415Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Inside Person class\n", "Name: Milaan Age: 33\n", "Inside Company class\n", "Name: Google location: Atlanta\n", "Inside Employee class\n", "Salary: 19000 Skill: Machine Learning\n" ] } ], "source": [ "# Example 1: Multiple Inheritance\n", "\n", "# Base class 1\n", "class Person:\n", " def person_info(self, name, age):\n", " print('Inside Person class')\n", " print('Name:', name, 'Age:', age)\n", "\n", "# Base class 2\n", "class Company:\n", " def company_info(self, company_name, location):\n", " print('Inside Company class')\n", " print('Name:', company_name, 'location:', location)\n", "\n", "# Derived class\n", "class Employee(Person, Company):\n", " def Employee_info(self, salary, skill):\n", " print('Inside Employee class')\n", " print('Salary:', salary, 'Skill:', skill)\n", "\n", "# Create object of Employee\n", "emp = Employee()\n", "\n", "# access data\n", "emp.person_info('Milaan', 33)\n", "emp.company_info('Google', 'Atlanta')\n", "emp.Employee_info(19000, 'Machine Learning')" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "ExecuteTime": { "end_time": "2021-06-20T19:10:31.849192Z", "start_time": "2021-06-20T19:10:31.830639Z" }, "scrolled": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "first\n", "second\n", "third\n" ] } ], "source": [ "# Example 2:\n", "\n", "class First(object): # Base class1\n", " def __init__(self):\n", " super(First, self).__init__()\n", " print(\"first\")\n", "\n", "class Second(object): # Base class2\n", " def __init__(self):\n", " super(Second, self).__init__()\n", " print(\"second\")\n", "\n", "class Third(Second, First): # Derived class derived from Base class 1 and Base class 2\n", " def __init__(self):\n", " super(Third, self).__init__()\n", " print(\"third\")\n", "\n", "Third(); #call Third class constructor" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "ExecuteTime": { "end_time": "2021-06-20T19:10:32.497625Z", "start_time": "2021-06-20T19:10:32.481024Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Dog has four legs.\n", "Dog is a warm-blooded animal.\n" ] } ], "source": [ "# Example 3:\n", "\n", "class Mammal(object):\n", " def __init__(self, mammalName):\n", " print(mammalName, 'is a warm-blooded animal.')\n", " \n", "class Dog(Mammal):\n", " def __init__(self):\n", " print('Dog has four legs.')\n", " super().__init__('Dog')\n", " \n", "d1 = Dog()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Explanation**:\n", "\n", "Here, we called the **`__init__()`** method of the **`Mammal`** class (from the **`Dog`** class) using code\n", "\n", "**`super().__init__('Dog')`**\n", "\n", "instead of\n", "\n", "**`Mammal.__init__(self, 'Dog')`**\n", "\n", "Since we do not need to specify the name of the base class when we call its members, we can easily change the base class name (if we need to).\n", "\n", "```python\n", "# changing base class to CanidaeFamily\n", "class Dog(CanidaeFamily):\n", " def __init__(self):\n", " print('Dog has four legs.')\n", "\n", " # no need to change this\n", " super().__init__('Dog')\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The **[super()](https://github.com/milaan9/04_Python_Functions/blob/main/002_Python_Functions_Built_in/068_Python_super%28%29.ipynb)** built-in function returns a proxy object, a substitute object that can call methods of the base class via delegation. This is called indirection (ability to reference base object with **`super()`** built-in function).\n", "\n", "Since the indirection is computed at the runtime, we can use different base classes at different times (if we need to)." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "ExecuteTime": { "end_time": "2021-06-20T19:10:34.033744Z", "start_time": "2021-06-20T19:10:34.014214Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Dog has 4 legs.\n", "Dog can't swim.\n", "Dog can't fly.\n", "Dog is a warm-blooded animal.\n", "Dog is an animal.\n", "\n", "Bat can't swim.\n", "Bat is a warm-blooded animal.\n", "Bat is an animal.\n" ] } ], "source": [ "# Example 4:\n", "\n", "class Animal:\n", " def __init__(self, Animal):\n", " print(Animal, 'is an animal.');\n", "\n", "class Mammal(Animal): # Mammal derived to Animal\n", " def __init__(self, mammalName):\n", " print(mammalName, 'is a warm-blooded animal.')\n", " super().__init__(mammalName)\n", " \n", "class NonWingedMammal(Mammal): # NonWingedMammal derived from Mammal (derived from Animal)\n", " def __init__(self, NonWingedMammal):\n", " print(NonWingedMammal, \"can't fly.\")\n", " super().__init__(NonWingedMammal)\n", "\n", "class NonMarineMammal(Mammal): # NonMarineMammal derived from Mammal (derived from Animal)\n", " def __init__(self, NonMarineMammal):\n", " print(NonMarineMammal, \"can't swim.\")\n", " super().__init__(NonMarineMammal)\n", "\n", "class Dog(NonMarineMammal, NonWingedMammal): # Dog derived from NonMarineMammal and NonWingedMammal\n", " def __init__(self):\n", " print('Dog has 4 legs.');\n", " super().__init__('Dog')\n", " \n", "d = Dog()\n", "print('')\n", "bat = NonMarineMammal('Bat')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Why `super()` keyword\n", "\n", "The **`super()`** function is most commonly used with **`__init__`** function in base class. This is usually the only place where we need to do some things in a child then complete the **initialization** in the **parent**.\n", "\n", "```python\n", "class Child(Parent):\n", " def __init__(self, stuff)\n", " self.stuff = stuff\n", " super(Child, self).__init__()\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Private members of parent class\n", "\n", "We don’t always want the instance variables of the parent class to be inherited by the child class i.e. we can make some of the instance variables of the parent class private, which won’t be available to the child class. We can make an instance variable by adding double underscores before its name. " ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "ExecuteTime": { "end_time": "2021-06-20T19:10:36.334505Z", "start_time": "2021-06-20T19:10:36.021035Z" }, "scrolled": false }, "outputs": [ { "ename": "AttributeError", "evalue": "type object 'D' has no attribute 'd'", "output_type": "error", "traceback": [ "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[1;31mAttributeError\u001b[0m Traceback (most recent call last)", "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[0;32m 16\u001b[0m \u001b[0mobject1\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mD\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 17\u001b[0m \u001b[1;31m# produces an error as d is private instance variable\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 18\u001b[1;33m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mD\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0md\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[1;31mAttributeError\u001b[0m: type object 'D' has no attribute 'd'" ] } ], "source": [ "## Example:\n", "\n", "# Python program to demonstrate private members\n", "# of the parent class\n", "class C(object):\n", " def __init__(self):\n", " self.c = 21\n", " # d is private instance variable\n", " self.__d = 42 # Note: before 'd' there are two '_'\n", "\n", "class D(C):\n", " def __init__(self):\n", " self.e = 84\n", " C.__init__(self)\n", "\n", "object1 = D()\n", "# produces an error as d is private instance variable\n", "print(D.d)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Python Multilevel Inheritance\n", "\n", "In multilevel inheritance, we can also inherit from a derived class. It can be of any depth in Python.\n", "\n", "All the features of the base class and the derived class are inherited into the new derived class.\n", "\n", "For example, there are three classes A, B, C. A is the superclass, B is the child class of A, C is the child class of B. In other words, we can say a **chain of classes** is called multilevel inheritance.\n", "\n", "
\n", "\n", "
\n", "\n", "\n", "Here, the **`Derived1`** class is derived from the **`Base`** class, and the **`Derived2`** class is derived from the **`Derived1`** class." ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "ExecuteTime": { "end_time": "2021-06-20T19:10:37.424343Z", "start_time": "2021-06-20T19:10:37.413604Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Inside Vehicle class\n", "Inside Car class\n", "Inside SportsCar class\n" ] } ], "source": [ "# Example 1:\n", "\n", "# Base class\n", "class Vehicle:\n", " def Vehicle_info(self):\n", " print('Inside Vehicle class')\n", "\n", "# Child class\n", "class Car(Vehicle):\n", " def car_info(self):\n", " print('Inside Car class')\n", "\n", "# Child class\n", "class SportsCar(Car):\n", " def sports_car_info(self):\n", " print('Inside SportsCar class')\n", "\n", "# Create object of SportsCar\n", "s_car = SportsCar()\n", "\n", "# access Vehicle's and Car info using SportsCar object\n", "s_car.Vehicle_info()\n", "s_car.car_info()\n", "s_car.sports_car_info()\n" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "ExecuteTime": { "end_time": "2021-06-20T19:10:38.178241Z", "start_time": "2021-06-20T19:10:38.156761Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Eating...\n", "Barking...\n", "Weeping...\n" ] } ], "source": [ "# Example 2:\n", "\n", "class Animal: # grandparent class\n", " def eat(self):\n", " print('Eating...')\n", "\n", "class Dog(Animal): # parent class\n", " def bark(self):\n", " print('Barking...')\n", "\n", "class BabyDog(Dog): # child class\n", " def weep(self):\n", " print('Weeping...')\n", "\n", "d=BabyDog()\n", "d.eat()\n", "d.bark()\n", "d.weep()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Hierarchical Inheritance\n", "\n", "In Hierarchical inheritance, more than one child class is derived from a single parent class. In other words, we can say one base class and multiple derived classes.\n", "\n", "
\n", "\n", "
" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "ExecuteTime": { "end_time": "2021-06-20T19:10:39.548350Z", "start_time": "2021-06-20T19:10:39.526867Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "This is Vehicle\n", "Car name is: BMW\n", "This is Vehicle\n", "Truck name is: Ford\n" ] } ], "source": [ "# Example 1:\n", "\n", "class Vehicle:\n", " def info(self):\n", " print(\"This is Vehicle\")\n", "\n", "class Car(Vehicle):\n", " def car_info(self, name):\n", " print(\"Car name is:\", name)\n", "\n", "class Truck(Vehicle):\n", " def truck_info(self, name):\n", " print(\"Truck name is:\", name)\n", "\n", "obj1 = Car()\n", "obj1.info()\n", "obj1.car_info('BMW')\n", "\n", "obj2 = Truck()\n", "obj2.info()\n", "obj2.truck_info('Ford')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Hybrid Inheritance\n", "\n", "When inheritance is consists of multiple types or a combination of different inheritance is called hybrid inheritance.\n", "\n", "
\n", "\n", "
" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "ExecuteTime": { "end_time": "2021-06-20T19:10:40.928707Z", "start_time": "2021-06-20T19:10:40.909180Z" }, "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Inside Vehicle class\n", "Inside Car class\n", "Inside SportsCar class\n" ] } ], "source": [ "# Example 1:\n", "\n", "class Vehicle:\n", " def vehicle_info(self):\n", " print(\"Inside Vehicle class\")\n", "\n", "class Car(Vehicle):\n", " def car_info(self):\n", " print(\"Inside Car class\")\n", "\n", "class Truck(Vehicle):\n", " def truck_info(self):\n", " print(\"Inside Truck class\")\n", "\n", "# Sports Car can inherits properties of Vehicle and Car\n", "class SportsCar(Car, Vehicle):\n", " def sports_car_info(self):\n", " print(\"Inside SportsCar class\")\n", "\n", "# create object\n", "s_car = SportsCar()\n", "\n", "s_car.vehicle_info()\n", "s_car.car_info()\n", "s_car.sports_car_info()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Example of Inheritance in Python\n", "\n", "To demonstrate the use of inheritance, let us take an example.\n", "\n", "A polygon is a closed figure with 3 or more sides. Say, we have a class called **`Polygon`** defined as follows." ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "ExecuteTime": { "end_time": "2021-06-20T19:10:42.466782Z", "start_time": "2021-06-20T19:10:42.446277Z" } }, "outputs": [], "source": [ "class Polygon:\n", " def __init__(self, no_of_sides):\n", " self.n = no_of_sides\n", " self.sides = [0 for i in range(no_of_sides)]\n", "\n", " def inputSides(self):\n", " self.sides = [float(input(\"Enter side \"+str(i+1)+\" : \")) for i in range(self.n)]\n", "\n", " def dispSides(self):\n", " for i in range(self.n):\n", " print(\"Side\",i+1,\"is\",self.sides[i])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "This class has data attributes to store the number of sides **`n`** and magnitude of each side as a list called **`sides`**.\n", "\n", "The **`inputSides()`** method takes in the magnitude of each side and **`dispSides()`** displays these side lengths.\n", "\n", "A triangle is a polygon with 3 sides. So, we can create a class called **`Triangle`** which inherits from **`Polygon`**. This makes all the attributes of **`Polygon`** class available to the **`Triangle`** class.\n", "\n", "We don't need to define them again (code reusability). **`Triangle`** can be defined as follows." ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "ExecuteTime": { "end_time": "2021-06-20T19:10:44.046846Z", "start_time": "2021-06-20T19:10:44.028297Z" } }, "outputs": [], "source": [ "class Triangle(Polygon):\n", " def __init__(self):\n", " Polygon.__init__(self,3)\n", "\n", " def findArea(self):\n", " a, b, c = self.sides\n", " # calculate the semi-perimeter\n", " s = (a + b + c) / 2\n", " area = (s*(s-a)*(s-b)*(s-c)) ** 0.5\n", " print('The area of the triangle is %0.2f' %area)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "However, class **`Triangle`** has a new method **`findArea()`** to find and print the area of the triangle. Here is a sample run." ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "ExecuteTime": { "end_time": "2021-06-20T19:11:05.921680Z", "start_time": "2021-06-20T19:10:52.909576Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Enter side 1 : 5\n", "Enter side 2 : 3\n", "Enter side 3 : 6\n" ] } ], "source": [ "t = Triangle()\n", "t.inputSides()" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "ExecuteTime": { "end_time": "2021-06-20T19:11:09.085226Z", "start_time": "2021-06-20T19:11:09.064722Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Side 1 is 5.0\n", "Side 2 is 3.0\n", "Side 3 is 6.0\n" ] } ], "source": [ "t.dispSides()" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "ExecuteTime": { "end_time": "2021-06-20T19:11:10.205335Z", "start_time": "2021-06-20T19:11:10.189714Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The area of the triangle is 7.48\n" ] } ], "source": [ "t.findArea()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can see that even though we did not define methods like **`inputSides()`** or **`dispSides()`** for class **`Triangle`** separately, we were able to use them.\n", "\n", "If an attribute is not found in the class itself, the search continues to the base class. This repeats recursively, if the base class is itself derived from other classes." ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "ExecuteTime": { "end_time": "2021-06-20T19:11:13.287342Z", "start_time": "2021-06-20T19:11:13.262931Z" }, "scrolled": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Calling child constructor\n", "Calling child method\n", "Calling parent method\n", "Parent attribute : 200\n" ] } ], "source": [ "# Example 1:\n", "\n", "class Parent: # define parent class\n", " parentAttr = 100\n", "\n", " def __init__(self):\n", " print (\"Calling parent constructor\")\n", "\n", " def parentMethod(self):\n", " print ('Calling parent method')\n", "\n", " def setAttr(self, attr):\n", " Parent.parentAttr = attr\n", "\n", " def getAttr(self):\n", " print (\"Parent attribute :\", Parent.parentAttr)\n", "\n", "class Child(Parent): # define child class\n", " def __init__(self):\n", " print (\"Calling child constructor\")\n", "\n", " def childMethod(self):\n", " print ('Calling child method')\n", "\n", "c = Child() # instance of child\n", "c.childMethod() # child calls its method\n", "c.parentMethod() # calls parent's method\n", "c.setAttr(200) # again call parent's method\n", "c.getAttr() # again call parent's method" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Explanation:**\n", "\n", "In the above example, **hierarchical** and **multiple** inheritance exists. Here we created, parent class **`Vehicle`** and two child classes named **`Car`** and **`Truck`** this is hierarchical inheritance.\n", "\n", "Another is **`SportsCar`** inherit from two parent classes named **`Car`** and **`Vehicle`**. This is multiple inheritance." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Python `super()` function\n", "\n", "When a class inherits all properties and behavior from the parent class is called inheritance. In such a case, the inherited class is a subclass and the latter class is the parent class.\n", "\n", "In child class, we can refer to parent class by using the **`super()`** function. **[Python super() function](https://rhettinger.wordpress.com/2011/05/26/super-considered-super/)** returns a temporary object of the parent class that allows us to call a parent class method inside a child class method.\n", "\n", "Benefits of using the **`super()`** function are:\n", "\n", "* We are not required to remember or specify the parent class name to access its methods.\n", "* We can use the **`super()`** function in both **single** and **multiple** inheritances.\n", "* The **`super()`** function support code **reusability** as there is no need to write the entire function" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "ExecuteTime": { "end_time": "2021-06-20T19:11:16.481163Z", "start_time": "2021-06-20T19:11:16.462613Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Arthur works at Google\n" ] } ], "source": [ "# Example 1:\n", "\n", "class Company:\n", " def company_name(self):\n", " return 'Google'\n", "\n", "class Employee(Company):\n", " def info(self):\n", " # Calling the superclass method using super()function\n", " c_name = super().company_name()\n", " print(\"Arthur works at\", c_name)\n", "\n", "# Creating object of child class\n", "emp = Employee()\n", "emp.info()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Explanation:**\n", "\n", "In the above example, we create a parent class **`Company`** and child class **`Employee`**. In Employee class, we call the parent class method by using a **`super()`** function." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `issubclass()`\n", "\n", "In Python, we can verify whether a particular class is a subclass of another class. For this purpose, we can use **[Python issubclass() function](https://github.com/milaan9/04_Python_Functions/blob/main/002_Python_Functions_Built_in/036_Python_issubclass().ipynb)**. This function returns **`True`** if the given class is the subclass of the specified class. Otherwise, it returns **`False`**.\n", "\n", "**Syntax:**\n", "\n", "```python\n", "issubclass(class, classinfo)\n", "```\n", "Where,\n", "\n", "* **`class`**: class to be checked.\n", "* **`classinfo`**: a class, type, or a tuple of classes or data types." ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "ExecuteTime": { "end_time": "2021-06-20T19:11:19.120794Z", "start_time": "2021-06-20T19:11:19.103217Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "True\n", "False\n", "False\n", "True\n", "True\n" ] } ], "source": [ "# Example 1:\n", "\n", "class Company:\n", " def fun1(self):\n", " print(\"Inside parent class\")\n", "\n", "class Employee(Company):\n", " def fun2(self):\n", " print(\"Inside child class.\")\n", "\n", "class Player:\n", " def fun3(self):\n", " print(\"Inside Player class.\")\n", "\n", "# Result True\n", "print(issubclass(Employee, Company))\n", "\n", "# Result False\n", "print(issubclass(Employee, list))\n", "\n", "# Result False\n", "print(issubclass(Player, Company))\n", "\n", "# Result True\n", "print(issubclass(Employee, (list, Company)))\n", "\n", "# Result True\n", "print(issubclass(Company, (list, Company)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Method Overriding\n", "\n", "In inheritance, all members available in the parent class are by default available in the child class. If the child class does not satisfy with parent class implementation, then the child class is allowed to redefine that method by extending additional functions in the child class. This concept is called **method overriding**.\n", "\n", "When a child class method has the same name, same parameters, and same return type as a method in its superclass, then the method in the child is said to **override** the method in the parent class.\n", "\n", "
\n", "\n", "
" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "ExecuteTime": { "end_time": "2021-06-20T19:11:20.977224Z", "start_time": "2021-06-20T19:11:20.958672Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "max speed is 200 Km/Hour\n" ] } ], "source": [ "# Example 1:\n", "\n", "class Vehicle:\n", " def max_speed(self):\n", " print(\"max speed is 100 Km/Hour\")\n", "\n", "class Car(Vehicle):\n", " # overridden the implementation of Vehicle class\n", " def max_speed(self):\n", " print(\"max speed is 200 Km/Hour\")\n", "\n", "# Creating object of Car class\n", "car = Car()\n", "car.max_speed()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Explanation:**\n", "\n", "In the above example, we create two classes named **`Vehicle`** (Parent class) and **`Car`** (Child class). The class **`Car`** extends from the class **`Vehicle`** so, all properties of the parent class are available in the child class. In addition to that, the child class redefined the method **`max_speed()`**." ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "ExecuteTime": { "end_time": "2021-06-20T19:11:23.111973Z", "start_time": "2021-06-20T19:11:23.103186Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Calling child method\n" ] } ], "source": [ "# Example 2:\n", "\n", "class Parent: # define parent class\n", " def myMethod(self):\n", " print ('Calling parent method')\n", "\n", "\n", "class Child(Parent): # define child class\n", " def myMethod(self):\n", " print ('Calling child method')\n", "\n", "\n", "c = Child() # instance of child\n", "c.myMethod() # child calls overridden method" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "ExecuteTime": { "end_time": "2021-06-20T19:11:24.170070Z", "start_time": "2021-06-20T19:11:24.133941Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Bird is ready\n", "Penguin is ready\n", "Penguin\n", "Swim faster\n", "Run faster\n" ] }, { "data": { "text/plain": [ "True" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Example 3:\n", "\n", "# parent class\n", "class Bird:\n", " \n", " def __init__(self):\n", " print(\"Bird is ready\")\n", "\n", " def whoisThis(self):\n", " print(\"Bird\")\n", "\n", " def swim(self):\n", " print(\"Swim faster\")\n", "\n", "# child class\n", "class Penguin(Bird):\n", "\n", " def __init__(self):\n", " # call super() function to run the __init__() method of the parent class inside the child class.\n", " super().__init__()\n", " print(\"Penguin is ready\")\n", "\n", " def whoisThis(self):\n", " print(\"Penguin\")\n", "\n", " def run(self):\n", " print(\"Run faster\")\n", "\n", "peggy = Penguin()\n", "peggy.whoisThis()\n", "peggy.swim()\n", "peggy.run()\n", "\n", "#issubclass(Penguin, Bird) \n", "isinstance(peggy, Bird)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Example of Method Overriding in Python\n", "\n", "In the example of **\"Polygon\"** and **\"Triangle\"**, 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 **`__init__`** in **`Polygon`**.\n", "\n", "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`**)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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. To learn more about the **`super()`** function in Python, visit **[Python super() function](https://rhettinger.wordpress.com/2011/05/26/super-considered-super/)**." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Two built-in functions **`isinstance()`** and **`issubclass()`** are used to check inheritances.\n", "\n", "The 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." ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "ExecuteTime": { "end_time": "2021-06-20T19:11:30.224709Z", "start_time": "2021-06-20T19:11:30.207135Z" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "isinstance(t,Triangle)" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "ExecuteTime": { "end_time": "2021-06-20T19:11:31.070407Z", "start_time": "2021-06-20T19:11:31.062598Z" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "isinstance(t,Polygon)" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "ExecuteTime": { "end_time": "2021-06-20T19:11:31.733488Z", "start_time": "2021-06-20T19:11:31.719820Z" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "isinstance(t,int)" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "ExecuteTime": { "end_time": "2021-06-20T19:11:32.469320Z", "start_time": "2021-06-20T19:11:32.448815Z" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "isinstance(t,object)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Similarly, **`issubclass()`** is used to check for class inheritance." ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "ExecuteTime": { "end_time": "2021-06-20T19:11:33.715406Z", "start_time": "2021-06-20T19:11:33.703687Z" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "issubclass(Polygon,Triangle)" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "ExecuteTime": { "end_time": "2021-06-20T19:11:34.378489Z", "start_time": "2021-06-20T19:11:34.370676Z" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "issubclass(Triangle,Polygon)" ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "ExecuteTime": { "end_time": "2021-06-20T19:11:35.701719Z", "start_time": "2021-06-20T19:11:35.688047Z" }, "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "issubclass(bool,int)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Also, see **[Python isinstance()](https://github.com/milaan9/04_Python_Functions/blob/main/002_Python_Functions_Built_in/034_Python_isinstance%28%29.ipynb)**." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Method Resolution Order in Python\n", "\n", "In Python, Method Resolution Order(MRO) is the order by which **Python looks for a method or attribute**. First, the method or attribute is searched within a class, and then it follows the order we specified while inheriting.\n", "\n", "This order is also called the Linearization of a class, and a set of rules is called MRO (Method Resolution Order). The **MRO plays an essential role in multiple inheritances as a single method may found in multiple parent classes**.\n", "\n", "In multiple inheritance, the following search order is followed.\n", "\n", "1. First, it searches in the current parent class if not available, then searches in the parents class specified while inheriting (that is left to right.)\n", "2. We can get the MRO of a class. For this purpose, we can use either the **`mro`** attribute or the **`mro()`** method." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Example:**\n", "\n", "```python\n", "class A:\n", " def process(self):\n", " print(\" In class A\")\n", "\n", "class B(A):\n", " def process(self):\n", " print(\" In class B\")\n", "\n", "class C(B, A):\n", " def process(self):\n", " print(\" In class C\")\n", "\n", "# Creating object of C class\n", "C1 = C()\n", "C1.process()\n", "print(C.mro())\n", "# In class C\n", "# [, , , ]\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Explanation:**\n", "\n", "In the above example, we create three classes named **`A`**, **`B`** and **`C`**. Class **`B`** is inherited from **`A`**, class **`C`** inherits from **`B`** and **`A`**. When we create an object of the **`C`** class and calling the **`process()`** method, Python looks for the **`process()`** method in the current class in the **`C`** class itself.\n", "\n", "Then search for parent classes, namely **`B`** and **`A`**, because **`C`** class inherit from **`B`** and **`A`**. that is, **`C(B, A)`** and always search in **left to right manner**." ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "ExecuteTime": { "end_time": "2021-06-20T19:11:44.127920Z", "start_time": "2021-06-20T19:11:44.120109Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "True\n" ] } ], "source": [ "# Output: True\n", "print(issubclass(list,object))" ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "ExecuteTime": { "end_time": "2021-06-20T19:11:44.957013Z", "start_time": "2021-06-20T19:11:44.943347Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "True\n" ] } ], "source": [ "# Output: True\n", "print(isinstance(5.5,object))" ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "ExecuteTime": { "end_time": "2021-06-20T19:11:45.725565Z", "start_time": "2021-06-20T19:11:45.718729Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "True\n" ] } ], "source": [ "# Output: True\n", "print(isinstance(\"Hello\",object))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the multiple inheritance scenario, any specified attribute is searched first in the current class. If not found, the search continues into parent classes in depth-first, left-right fashion without searching the same class twice.\n", "\n", "So, in the above example of **`MultiDerived`** class the search order is [**`MultiDerived`**, **`Base1`**, **`Base2`**, **`object`**]. This order is also called linearization of **`MultiDerived`** class and the set of rules used to find this order is called **Method Resolution Order (MRO)**.\n", "\n", "MRO must prevent local precedence ordering and also provide monotonicity. It ensures that a class always appears before its parents. In case of multiple parents, the order is the same as tuples of base classes.\n", "\n", "MRO of a class can be viewed as the **`__mro__`** attribute or the **`mro()`** method. The former returns a tuple while the latter returns a list\n", "\n", "```python\n", ">>> MultiDerived.__mro__\n", "(,\n", " ,\n", " ,\n", " )\n", "\n", ">>> MultiDerived.mro()\n", "[,\n", " ,\n", " ,\n", " ]\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here is a little more complex multiple inheritance example and its visualization along with the MRO.\n", "\n", "
\n", "\n", "
" ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "ExecuteTime": { "end_time": "2021-06-20T19:11:48.932575Z", "start_time": "2021-06-20T19:11:48.919877Z" }, "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[, , , , , , ]\n" ] } ], "source": [ "# Demonstration of MRO\n", "\n", "class X:\n", " pass\n", "\n", "\n", "class Y:\n", " pass\n", "\n", "\n", "class Z:\n", " pass\n", "\n", "\n", "class A(X, Y):\n", " pass\n", "\n", "\n", "class B(Y, Z):\n", " pass\n", "\n", "\n", "class M(B, A, Z):\n", " pass\n", "\n", "# Output:\n", "# [, ,\n", "# , ,\n", "# , ,\n", "# ]\n", "\n", "print(M.mro())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To know the actual algorithm on how MRO is calculated, visit [**Discussion on MRO**](https://www.python.org/download/releases/2.3/mro/)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "hide_input": false, "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.8" }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": false, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": false, "toc_position": {}, "toc_section_display": true, "toc_window_display": false }, "varInspector": { "cols": { "lenName": 16, "lenType": 16, "lenVar": 40 }, "kernels_config": { "python": { "delete_cmd_postfix": "", "delete_cmd_prefix": "del ", "library": "var_list.py", "varRefreshCmd": "print(var_dic_list())" }, "r": { "delete_cmd_postfix": ") ", "delete_cmd_prefix": "rm(", "library": "var_list.r", "varRefreshCmd": "cat(var_dic_list()) " } }, "types_to_exclude": [ "module", "function", "builtin_function_or_method", "instance", "_Feature" ], "window_display": false } }, "nbformat": 4, "nbformat_minor": 2 }