Transcripted Summary

In this video we'll learn about inheritance, multiple inheritance and overriding methods or polymorphism.


# Inheritance

Inheritance is when we use the attributes and methods from the parent class and make those attributes and methods available to the child's class.

In our sample code below, we have a Person class that has the initialization with self, attribute and attribute2.



You'll notice that in the Enemy class, we use the Person class as the parent or base class, and then we have our initialization method, and we include all of the attributes that we would like to have in our Enemy class.


In order to access all of those attributes, we use the super method.

In the super method, all of the attributes of the parent class are accessed. Then we can set any new attributes in the child's class.

We'll build on the Person class we made in the class video and use the class to learn about inheritance and polymorphism.

Please start this exercise by opening your person.py file that you used in the classes video. You'll scroll down to the bottom of the file and start by making a new class.

Remember that our class syntax is the word “class” followed by the class name.

We have a class called Enemy and we want this Enemy class to inherit from the Person class.

We want an enemy to be able to do all of the things a person does, so in the parentheses after the class name Enemy, we'll type the class Person, since we want the enemy to be able to use the attributes and methods that we made in the Person class.

Then we'll go ahead and add a colon to the end of our class.

As a reminder, let's scroll up to the person class in recall the attributes and methods from that class.

  • We had Person.introduce, Person.emote, and Person.status_change as our methods.

  • We had first name, last name, health and status as attributes.

We'll use all of the attributes in our Enemy class.

Now, let's scroll back down to our Enemy class.


Since we have a new class, we'll want to use this class to make Enemy Objects.

To make these Objects, we'll need an __init__ method or constructor for this Enemy class. We'll write the first line of the method to contain every attribute that the method will have.

We know that our first name, last name, health and status attributes come from the Person class, so instead of reassigning those attributes, we'll use the super method, which will initialize those attributes as they are defined in the parent class.


class Enemy(Person):
    def __init__(self, weapon, firstname, lastname, health, status):
        super().__init__(firstname, lastname, health, status)
        self.weapon = weapon

You'll notice that in our super method, we've called the first name, last name, health and status attributes.

Because the weapon attribute is a new attribute to the Enemy Object, we'll need to use the self argument and self.weapon to equal “weapon” so that we can access the new attribute in all of our Enemy class methods.

Now that we've created our Enemy constructor or __init__ method for our Enemy class, we can create some new methods for our Enemy class.


Since we are creating an enemy, our first method will be the Hurt method.

Well, define our method the same way we've defined all the other methods in our classes before.

In this method, we'll use the conditionals to look at what weapon the enemy has and if the enemy has a certain weapon, then they will deal a certain amount of damage. This is a mechanic that you might recognize if you are a video game player.

We'll start with our if statement and then elif statement and then we'll print our output.


def hurt(self, other):

    if self.weapon == 'rock':
        other.health -= 10

    elif self.weapon == 'stick':
        other.health -= 5

    print(other.health)

You'll notice that my is if statement asks the program to compare the weapon that is assigned, and if the weapon of the enemy is a rock, then the person who is the other will have their health reduced by 10.

You'll notice that I'm using the Python Decrement Operator (-=).


The next method I make will be the insult method.

You'll notice that my methods in the Enemy class take 2 arguments: the self argument and the “other” argument.


def insult(self, other):
    if other.health <= 80:
        print("{}, you are tired and weak".format(other.firstname))

The self argument allows access to the enemy and the “other” argument allows access to the other person that I am interacting with.


Finally, we'll create the steal method.

The steal method will define an interaction between the enemy and the person.

The steal method will print a message and if the person was a friend, it will change their friend status to False.


def steal(self, other):
    print("ha ha ha, {}, I have your stuff!".format(other.firstname))

    if other.status == True:
        other.status = False

Now we have 3 methods in our Enemy class.


Let's unindent our code and create an enemy to interact with some of the people we've created.

Our enemy, Alex Wayne, will have a rock as a weapon and a health status of 75.


Alex = Enemy('rock', 'Alex', 'Wayne', 75, status = False)

Before I try out the class methods, I'd like to just go through and double check my syntax to try and catch any errors.

I'm looking for simple things like any kind of dot notation, single or double equals, colons and spacing. Some IDEs will give you some information.

Overall, this is looking good to me, so now I'd like to try this out.

I'm going to remind you that we have 3 people that we created in our class video. We have Maria, Rey and Lee. So, as a test of the methods, I'm going to have Alex interact with Maria, Rey and Lee.


First, I'll try the hurt method — Alex.hurt(Maria)

Since Alex has a rock, Maria should lose 10 points of health. Maria's current health is 95, so her health should be 85 if this method acts correctly.

Let's run the code and find out.



Excellent.

The last thing is 85, and that's the outcome I expected.


Next, I'd like to test the insult method — Alex.insult(Rey)

So, in the insult method, if the health is less than or equal to 80, then Alex will say, "You are tired and weak."

Rey's health is 88 so that message shouldn't print out, and you'll notice nothing's happened. The message has not printed out.

However, you'll notice that Lee's health was a little bit lower. Let's see if we get a message for Lee.



Finally, we will use our steal method and test that out — Alex.steal(Rey)



Excellent.

We can see that each of our methods that is inherited from the person class is able to use the different elements of first name, last name, health and status.

For Alex, it's able to give Alex a weapon and then we have specific methods that Alex can use.


It's important to know that the methods that we've created for our enemies are not available to our person class.

In order to test this out, we can try to call an Enemy method on one of our people.

For example, we can call Rey and use the steal method and put Alex's name.


# error condition
Rey.steal(Alex)

When we run this code, we should expect to see an error, and in fact we do see an error.



There's no message printed out to Alex and more importantly, we have an attribute error, which is that the Person Object (that's Rey) does not have an attribute steal.

There's no steal method for the person class, so this is also acting as we expect.


# Multiple Inheritance

Multiple inheritance is when one class inherits from multiple classes and is able to use attributes and methods from both classes.

There are pros and cons to using multiple inheritance.

Pros include the ability to reuse small amounts of code in multiple classes and mix-ins.

Cons include the order of inheritance matters. Inheriting from multiple classes can become quite complicated depending on the number of classes, the names of class methods and other factors, including common attributes shared among multiple parent classes.

There can be more maintenance involved when refactoring code that is using multiple inheritance.


There are 2 ways to do multiple inheritance in Python.

  • The first way is not as Pythonic and requires a bit more maintenance. However, it's easier to see exactly what's happening.

  • The second way is to continue to use the super method as we did with a single inheritance. However, this method can be complicated and quite confusing, so we won't use it in the video.

NOTE

You can do research on multiple inheritance in Python and using the super method and the method resolution order or MRO to learn more about how these things work in Python.

Typically, if we have multiple classes that we want to inherit from, we'll have the attributes of each class and then bring in the attributes of the class individually into the initialization or __init__ method of the child class.



This gives us access to the methods and attributes of both parent classes.


I've made a new file called multiple.py, and in this file, we're going to take a look at how to use multiple inheritance.

We'll create a simple example so you can see how each of the parent classes is used in the child class.

First, we'll create the parent class. We'll call this class Item.

In this class, we will create initialization or __init__ method that has the self and a SKU number.


# Parent class 1
class Item():

    def __init__(self, sku):
        self.sku = sku

    def print_sku(self):
        print("The sku is {}.".format(self.sku))

Now we'll create the second parent class. This class will be called the Garment class.

In the __init__ or constructor method, we will have a section and a type.


# Parent class 2
class Garment():

    def __init__(self, section, type):
        self.section = section
        self.type = type

    def print_garment(self):
        print("The garment is in section {}, {}.".format(self.section, self.type))

Finally, we'll create our child class, which will inherit from both of the parents.

In our initialization or constructor method, we'll identify all of the attributes from both parent classes, in addition to the attributes that are specific to the child's class.

  • First, we'll define the attributes that are specific to the child's class.

  • Now, we'll initialize the attributes from parent class 1, the Item class.

  • Next, we'll initialize the attributes from parent class 2, the Garment class.



# Child Class
class Shirts(Item, Garment):

    def __init__(self, sku, section, type, name, color):
        self.name = name
        self.color = color

        Item.__init__(self, sku)
        Garment.__init__(self, section, type)

    def print_shirt(self):
        print("{} {} on sale!".format(self.color, self.name))

So, we have our basic structure of 2 parent classes and 1 child class, and now what I'd like to do is to create 1 method inside of each class to use as an example method.


I'm just going to print a basic message that lets me make sure that I'm getting the attribute that we've defined in the constructor and that I'm able to print that out.

Hopefully this will allow you to see how the child class uses and accesses the attributes and methods from the parent.

Okay, so I have 3 methods:

  • I have the parent class 1 method of print_sku

  • The parent class 2 method of print_garment

  • The child class method of print_shirt

So, first, let's make a child class item.


Blouse = Shirts("00001", 43, "Tops", "Formal Blouse", "White")

And now that I have the child Object, I can experiment with calling each of the parent methods.

We'll start with the print_sku method, then the print_garment method from the second parent, and finally, child method.


Blouse.print_sku()
Blouse.print_garment()
Blouse.print_shirt()

So, the child should have access to all 3 of these methods since it inherits from both parent classes.

You can see we have the print_sku method, the print_garment method, and the print_shirt method.



I hope that this simple example helps you to better understand how multiple inheritance works.


# Polymorphism — Method Overriding

Finally, we have polymorphism.

Polymorphism occurs when we want to allow the child class to have a method with the same name and a similar implementation as the parent class and we wish for that method you override the parent class method.

Let's see an example of polymorphism.

We have the introduce method, which we originally wrote in our Person class. We also made an Enemy class and the Enemy class uses both methods and attributes from the Person class.

What happens if we want to make a method called introduce for the Enemy class?

Let's go ahead and set up that method right out.

Now, instead of the nice introduction that we use for all people, our enemy is going to say something that's a little bit more aggressive.


def introduce(self):
    print("You are my mortal enemy!!!")

When we make a method in the child's class with the same name as the one in the parent class, the child class method overrides the parent class method.

Now, the Enemy Object runs its own code in the introduce method.

We can run our code and compare the Person.introduce method to the Enemy.introduce.


Maria.introduce()
Rey.introduce()
Alex.introduce()

So, we can see that we have, "Hello, my name is Maria.”, and “Hello, my name is Rey.", and those are our person introductions.



But our Enemy Object introduces by saying, "You are my mortal enemy."

This is the basics of how polymorphism or method overriding work in Python.



Resources



© 2021 Applitools. All rights reserved. Terms and Conditions Privacy Policy GDPR