Class Inheritance

In this tutorial, you will learn an advanced concept in Object-Oriented Programming: Class Inheritance

Class Inheritance Introduction



Sometimes, you need a lot of different object types that do mostly the same things, but have some minor differences.

As an example, here is a Person class.

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
	
	def introduce(self):
        print("Hi, my name is " + self.name + " and I am " + str(self.age) + " years old.")
		
example_person = Person("Jason", 15)
example_person.introduce()

# Output
# Hi, my name is Jason and I am 15 years old.
			


But now, say that you want to have people who have other attributes or methods in addition to this variables. For example, you want to create a student that will go to school, and an Adult that will go to work. You still want both of them to be able to introduce themselves, but you want to add more functionality to the class depending on if they are a student or adult.

You can do this by using a concept called inheritance. You can create another class that still has all the methods of the Person class, but will add additional methods and functionality to the class.

In the Adult class, the class Person is put in parentheses after the name of the Adult class, to indicate that this new class should have all the same functionality as that class.

In the __init__ method, the super().__init__(name, age)indicates that the __init__ method from the Person class should run, but another line has been added to define a new attribute of money.

Another method has been added called go_to_work. Every time the adult goes to work, they will gain 100 dollars.

In programming terms, the Person class is referred to as the Parent class and the Adult class is referred to as the Child class because it inherits from the Person class.

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
	
    def introduce(self):
        print("Hi, my name is " + self.name + " and I am " + str(self.age) + " years old.")

class Adult(Person):

    def __init__(self, name, age):
        super().__init__(name, age)
        self.money = 0

    def go_to_work(self):
        self.money += 100
        print(self.name + " now has " + str(self.money) + " dollars")

example_adult = Adult("Rob", 45)
example_adult.introduce()
example_adult.go_to_work()
example_adult.go_to_work()

# Output
# Hi, my name is Rob and I am 45 years old.
# Rob now has 100 dollars
# Rob now has 200 dollars
			

You can create as many classes as you want like this, and makes it easier to organize your code. For example, here is the Student class, which has a different method called go_to_school. This method will improve her grade point average every time she goes to school, up to a 4.0.

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
	
    def introduce(self):
        print("Hi, my name is " + self.name + " and I am " + str(self.age) + " years old.")

class Adult(Person):

    def __init__(self, name, age):
        super().__init__(name, age)
        self.money = 0

    def go_to_work(self):
        self.money += 100
        print(self.name + " now has " + str(self.money) + " dollars")
		
class Student(Person):

    def __init__(self, name, age):
        super().__init__(name, age)
        self.grade_point_average = 2.0

    def go_to_school(self):
        if (self.grade_point_average >= 4.0):
            self.grade_point_average = 4.0
        else:
            self.grade_point_average += 0.1
        print(self.name + " now has a " + str(self.grade_point_average) + " GPA")

example_student = Student("Amy", 8)
example_student.introduce()
example_student.go_to_school()
example_student.go_to_school()

# Output
# Hi, my name is Amy and I am 8 years old.
# Amy now has a 2.1 GPA
# Amy now has a 2.2 GPA
			


Importing Modules Reviewed

When you create multiple classes that are long, you can put them in different files



If you are creating a lot of different classes that are all very long, your file size might get to be really large.

Just like you used import to import packages that you used inside of your code, you can use import to import code that you've written.

Take the Person class and create a new file called person.py. This will be the code in that file.

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
	
    def introduce(self):
        print("Hi, my name is " + self.name + " and I am " + str(self.age) + " years old.")
			

Then, create a new file called adult.py, and write this code into that file. Make sure that the adult.py file is in the same location as the person.py file.

import person
			
class Adult(person.Person):

    def __init__(self, name, age):
        super().__init__(name, age)
        self.money = 0

    def go_to_work(self):
        self.money += 100
        print(self.name + " now has " + str(self.money) + " dollars")

example_adult = Adult("Rob", 45)
example_adult.introduce()
example_adult.go_to_work()
example_adult.go_to_work()

# Output
# Hi, my name is Rob and I am 45 years old.
# Rob now has 100 dollars
# Rob now has 200 dollars
			


The import statement will run all the code inside of the imported file. That means that you can use the code that is inside the person.py file in the same way that you would use it as if you imported a package like turtle.

from person import *
			
class Adult(Person):

    def __init__(self, name, age):
        super().__init__(name, age)
        self.money = 0

    def go_to_work(self):
        self.money += 100
        print(self.name + " now has " + str(self.money) + " dollars")

example_adult = Adult("Rob", 45)
example_adult.introduce()
example_adult.go_to_work()
example_adult.go_to_work()

# Output
# Hi, my name is Rob and I am 45 years old.
# Rob now has 100 dollars
# Rob now has 200 dollars
			


In the highlighted lines, you can see another way of importing from another file. When you use the format from person import *, you will be importing all of the content of the file just like if you copied and pasted the contents of that file to the top of your file. Then, you won't have to use dot notation to reference classes in that file.

Challenge 1 - Fighting Game

Before you start, download these three files: fighter.py, Arena.py, and FightingGame.py. Make sure all of the files are in the same location, then open FightingGame.py.



Run the FightingGame module and you can see two fighters fighting. The fighters are defined on lines 4 and 5 from objects outside of the FightingGame.py file, and are imported. An arena object is then used for them to fight in.

Look inside the Fighter.py and Arena.py files to learn more about the code for those objects.

You can create a new class inside of Fighter.py that is a child of the Fighter class. For example, the DualWieldingFighter does double damage when they attack.

import random

class Fighter:
    def __init__(self, name, health, damage):
        self.name = name
        self.health = health
        self.damage = damage


    def takeDamage(self, amount):
        self.health -= amount


    def attack(self):
        dmg = random.randrange(1, self.damage)
        return dmg

    def isDead(self):
        if self.health <= 0:
            return True

        return False


    def heal(self, amount):
        self.health += amount


    def ToString(self):
        output = ""

        output += "Name: " + self.name + "\n"
        output += "Health: " + str(self.health) + "\n"

        return output
		
class DualWieldingFighter(Fighter):

    def attack(self):
        dmg = random.randrange(1, self.damage) * 2
        return dmg
			


Then, update the fightinggame.py file to use your new child class.

from Arena import *
from Fighter import *

fighter1 = Fighter("Garry the Invincible", 100, 15)
fighter2 = DualWieldingFighter("Legolas", 85, 25)

theArena = Arena()

theArena.Fight(fighter1, fighter2)
			


Here's some ideas for different types of fighters you could try.

Try to create a new fighter child class called ArmoredFighterthat will also take in a defense argument when initialized. In their takeDamage method, their health will be reduced by the amount of damage minus their defense stat.

Create a DodgeFighter which will have a random chance to dodge any attack.

Create a ReviveFighter that has a random chance that they will be revived with extra health if they die.

There's lots of ways you can expand this concept. Consider making a giant tournament with the different fighter types and see who comes out on top.

Challenge 2 - Advanced Rock Paper Scissors AI

Before you start, download these two files: rpstournament.py and gameplayer.py



Open the gameplayer.py file and you will see an import statement at the top to import rpstournament, and code at the bottom that will create an object from that class, and run it through a method in an object created from rpstournmanent.

When you run the module, you will see results returned in the python shell. This code runs a game AI through a tournament against a bunch of pre-built AIs. The results show the win rate of the AI against all its opponents.

In Rock Paper Scissors, Always Play Random is a valid strategy. But against players with actual strategies, this AI will never go much beyond a 50% win rate.

Your challenge is to create an AI that inherits from the GamePlayer class and rewrites the methods to achieve a better win rate. If you are curious about the strategies of particular AIs, you can look inside the rpstournament.py file.

You can write as many different class definitions as you want, so try to make different AIs and compare them to see how they do.

You should be trying to create AIs that consistently perform better than a completely random AI.


Part 2: Other Real Players

Once you have a handle on playing against the included AIs, download the following AIs to the same folder that you have rpstournament.py.

These AIs were developed by former students in the class to try to beat the standard AIs in the tournament, and they use complicated strategies to try to beat their opponents. Are you up for the challenge?

Switcharoo AI - rpsai_switcharoo.py

Tiger Shark AI - rpsai_tigershark.py

Shallow Blue AI - rpsai_shallowblue.py