Five Types of Inheritance in Python

In a previous article, we introduced the topic of object-oriented programming, or OOP for short. Then, we discussed classes and the topic of inheritance. This article will do a quick recap of inheritance, what it is, and why you’d use it. Then we’ll introduce the different types of inheritance you might encounter in your programming and illustrate them with code examples. Finally, we’ll touch briefly on nested inheritance.

What Does Inheritance Mean?

So we know that a class is a blueprint of an object, and it contains attributes and methods. Inheritance refers to the ability of one class to inherit the attributes and methods of another. In effect, it gets to use those components by reference to the other class without needing to rewrite all the necessary code.

We call this association a Parent-Child relationship, where the child inherits the attributes of the parent. Yet, this inheritance may take many forms. In the real world, we are all familiar with a single direct inheritance where a child inherits from her father, which is indeed one of the forms used in Python. Yet, there are other more complicated forms that we’ll now discuss.

Introducing Five Types of Inheritance

Although the fifth type of inheritance is essentially an amalgam of the preceding four, there are five primary forms. I’ll show each example in block form, and then I’ll show it in code. Finally, we’ll use an example of a grocery store to explain the types.

Single Inheritance

The most basic form of inheritance, in this case, the child inherits from a single parent.

Single Inheritance

Let’s see this in a code example where we have a grocery store class containing generic attributes of grocery items which is the parent, and a child class of canned items which have attributes specifically related to cans, such as volume and manufacturer.

# Single Inheritance

class Stock:
    category = 'Groceries'

    def __init__(self, stock_code, description, buy_price, mark_up):
        self.code = stock_code
        self.desc = description
        self.buy = buy_price
        self.margin = mark_up

    def sell_price(self):
        print('Retail price = $', round(self.buy * self.margin, 2))

    def sale(self, discount):
        print('The discounted price of {} is $'.format(C298.desc),
              round(self.buy * self.margin * (1- discount), 2))

class Canned(Stock):
    category = 'Cans'

    def __init__(self, stock_code, description, buy_price, mark_up, volume, manuf):
       self.volume = volume
       self.manuf = manuf
       Stock.__init__(self, stock_code, description, buy_price, mark_up)

    def Label(self):
        print(self.desc, '\nVolume: ', self.volume)
        self.sell_price()

C298 = Canned('C298', 'Chicken Soup', 0.75, 1.553, '400 mls', 'Campbells')

C298.Label()

Here’s the result of this code snippet:

# Result

Chicken Soup 
Volume:  400 mls
Retail price = $ 1.16

In this example, you saw the simple relationship of single inheritance where attributes such as stock code, description etc., belonging to the parent class called Stock, are made available for use by the child, called Canned. Not only are the attributes accessible, so too are the methods within the parent. For example, all children of the Canned class may access and use the sell_price method of the parent.

Multiple Inheritance

Multiple inheritance occurs when there are two or more parent classes from which a child class may inherit.

Multiple Inheritance

We will extend our grocery store example to show multiple inheritances. In the following code, we have two parent classes, our original Stock and another called Warehouse. The Canned class is a child of both, inheriting the Stock attributes and methods and the Warehouse attributes, such as warehouse location, packaging, and quantity per pack.

# Multiple Inheritance

class Stock:
    category = 'Groceries'

    def __init__(self, stock_code, description, buy_price, mark_up):
        self.code = stock_code
        self.desc = description
        self.buy = buy_price
        self.margin = mark_up

    def sell_price(self):
        print('Retail price = $', round(self.buy * self.margin, 2))

    def sale(self, discount):
        print('The discounted price of {} is $'.format(C298.desc),
              round(self.buy * self.margin * (1- discount), 2))

class Warehouse:
    category = 'Store'

    def __init__(self, location, pack_type, qty_per_pack):
        self.locn = location
        self.pack_type = pack_type
        self.pack_qty = qty_per_pack

class Canned(Stock, Warehouse):
    category = 'Cans'

    def __init__(self, stock_code, description, buy_price, mark_up, volume, manuf, location, pack_type, qty_per_pack):
        self.volume = volume
        self.manuf = manuf
        Stock.__init__(self, stock_code, description, buy_price, mark_up)
        Warehouse.__init__(self, location, pack_type, qty_per_pack)

    def stock_label(self):
        print('Stock Code: {} \nDescription: {} \nManufacturer: {} '
              '\nStock Locn: {} \nPacking: {} \nQty/Pack: {}'
              .format(self.code, self.desc, self.manuf, self.locn, self.pack_type, self.pack_qty))


C298 = Canned('C298', 'Chicken Soup', 0.75, 1.553, '400 mls', 'Campbells', 'Bay 24C', 'Carton', 48)

C298.stock_label()

Let’s have a look at the result:

# Result

Stock Code: C298 
Description: Chicken Soup 
Manufacturer: Campbells 
Stock Locn: Bay 24C 
Packing: Carton 
Qty/Pack: 48

You can see from the code that when we printed the stock label, it contained attributes drawn from both parent classes while using a method from the child class.

Multilevel Inheritance

The power of inheritance is the ability for a child class to be the parent class of another. Thus, to stretch the analogy, we have a grandparent, parent, child relationship that describes multilevel inheritance.

Multilevel Inheritance

In our grocery store, I’m pushing the boundaries a bit with the example, but let’s imagine we have the Stock class as the grandparent, a Meat class as the parent, and a child class of Chicken. Probably not a realistic example, but I’m sure you get the point. Here’s the code.

# Multi-Level Inheritance

class Stock:
    category = 'Groceries'

    def __init__(self, stock_code, description, buy_price, mark_up):
        self.code = stock_code
        self.desc = description
        self.buy = buy_price
        self.margin = mark_up

    def sell_price(self):
        print('Retail price = $', round(self.buy * self.margin, 2))

    def sale(self, discount):
        print('The discounted price of {} is $'.format(C298.desc),
              round(self.buy * self.margin * (1 - discount), 2))

class Meat(Stock):
    category = 'Meat'

    def __init__(self, stock_code, description, buy_price, mark_up, weight, use_by):
        self.kilo = weight
        self.expiry = use_by
        Stock.__init__(self, stock_code, description, buy_price, mark_up)

    def Expiring(self, discount):
        print('Price reduced for quick sale: ${}'.format(round(self.buy * self.margin * (1 - discount), 2)))

    def Label(self):
        print(self.desc, '\nWeight: ', self.kilo, 'kgs', '\nExpiry: ', self.expiry)
        self.sell_price()

# C401 = Meat('C401', 'Sirloin Steak', 4.16, 1.654, .324, '15 June 2021')

class Chicken(Meat):
    category = 'Chicken'

    def __init__(self, stock_code, description, buy_price, mark_up, weight, use_by, portion, condition):
        self.cut = portion
        self.cond = condition
        Meat.__init__(self, stock_code, description, buy_price, mark_up, weight, use_by)

    def stock_label(self):
        print('Stock Code: {} \nDescription: {} \nPortion: {} '
              '\nCooked/Fresh/Frozen: {} \nWeight: {} kgs \nUse By: {}'
              .format(self.code, self.desc, self.cut, self.cond, self.kilo, self.expiry))

C793 = Chicken('C793', 'Chicken Pieces', 2.65, 1.756, 0.495, '28 July 2021', 'Drumsticks', 'Frozen' )

C793.stock_label()
print()
C793.sell_price()
print()
C793.Expiring(.20)

The result of this code snippet is as follows:

# Result

Stock Code: C793 
Description: Chicken Pieces 
Portion: Drumsticks 
Cooked/Fresh/Frozen: Frozen 
Weight: 0.495 kgs 
Use By: 28 July 2021

Retail price = $ 4.65

Price reduced for quick sale: $3.72

The Chicken child class has added two new parameters, portion and condition. The portion parameter describes drumsticks, thighs, breast, quarter, half, and whole, while the condition describes frozen, fresh or cooked. We pass these to attributes self.cut and self.cond. We access the other attributes from either the parent, Meat class or the grandparent, Stock class. We also use methods from all three class levels.

Hierarchical Inheritance

Hierarchical inheritance resembles the classic hierarchical structure of an organisation tree. It has a parent with multiple children.

Hierarchical Inheritance

With the grocery store example, the different product categories are all children to the parent Stock class. Thus, we have Canned, Meat, and Produce classes which will all draw from the parent Stock class for the generic attributes and methods. Yet each will add their attributes and methods specific to the particular needs of the category.

# Hierarchical Inheritance

class Stock:
    category = 'Groceries'

    def __init__(self, stock_code, description, buy_price, mark_up):
        self.code = stock_code
        self.desc = description
        self.buy = buy_price
        self.margin = mark_up

    def sell_price(self):
        print('Retail price = $', round(self.buy * self.margin, 2))

    def sale(self, discount):
        print('The discounted price of {} is $'.format(self.desc),
              round(self.buy * self.margin * (1 - discount), 2))


class Canned(Stock):
    category = 'Cans'

    def __init__(self, stock_code, description, buy_price, mark_up, volume, manuf):
        self.volume = volume
        self.manuf = manuf
        Stock.__init__(self, stock_code, description, buy_price, mark_up)


    def multi_buy(self):
        print('Buy two {} of {} {} {} and get one free. Pay only ${}'.format(self.category, self.manuf, \
                                                                             self.volume, self.desc, \
                                                                             round(self.buy * self.margin, 2)))

class Meat(Stock):
    category = 'Meat'

    def __init__(self, stock_code, description, buy_price, mark_up, weight, use_by):
        self.kilo = weight
        self.expiry = use_by
        Stock.__init__(self, stock_code, description, buy_price, mark_up)

    def Label(self):
        print(self.desc, '\nWeight: ', self.kilo, 'kgs', '\nExpiry: ', self.expiry)
        self.sell_price()

    def Expiring(self, discount):
        print('Price reduced for quick sale: ${}'.format(round(self.buy * self.margin * (1 - discount), 2)))

class Produce(Stock):
    category = 'Produce'

    def __init__(self, stock_code, description, buy_price, mark_up, category, unit):
        self.type = category
        self.unit = unit
        Stock.__init__(self, stock_code, description, buy_price, mark_up)

    def Label(self):
        print(self.desc, self.type, '\nPrice: $', (round(self.buy * self.margin, 2)), self.unit)


C401 = Meat('C401', 'Sirloin Steak', 4.16, 1.654, .324, '15 June 2021')

C298 = Canned('C298', 'Chicken Soup', 0.75, 1.553, '400 mls', 'Campbells')

C287 = Produce('C287', 'Golden Delicious', 0.25, 1.84, 'Apples', 'ea')

C401.Label()
print()
C401.Expiring(.35)
print()
C298.multi_buy()
print()
C298.sell_price()
print()
C287.Label()
print()
C287.sale(.15)

The result is:

# Result

Sirloin Steak 
Weight:  0.324 kgs 
Expiry:  15 June 2021
Retail price = $ 6.88

Price reduced for quick sale: $4.47

Buy two Cans of Campbells 400 mls Chicken Soup and get one free. Pay only $1.16

Retail price = $ 1.16

Golden Delicious Apples 
Price: $ 0.46 ea

The discounted price of Golden Delicious is $ 0.39

In this code, we called the specific attributes and methods of each child class while also successfully drawing on the attributes and methods of the parent.

Hybrid Inheritance

As you’ll guess, hybrid inheritance is simply an amalgam of the other types of inheritance.

Hybrid Inheritance

In our grocery store, we have the generic Stock class and the Warehouse class, which both flow to the class of Meat. Under the Meat class, we have Chicken, Pork, and Beef. As you can see, the inheritance type defies an obvious name, hence the catch-all of Hybrid.

# Hybrid Inheritance

class Stock: # Grandparent class
    category = 'Groceries'

    def __init__(self, stock_code, description, buy_price, mark_up):
        self.code = stock_code
        self.desc = description
        self.buy = buy_price
        self.margin = mark_up

    def sell_price(self):
        print('Retail price = $', round(self.buy * self.margin, 2))

class Warehouse: # Grandparent class
    category = 'Store'

    def __init__(self, location, pack_type, qty_per_pack):
        self.locn = location
        self.pack_type = pack_type
        self.pack_qty = qty_per_pack

class Meat(Stock, Warehouse): # Parent class
    category = 'Meat'

    def __init__(self, stock_code, description, buy_price, mark_up, location, pack_type, qty_per_pack, weight, use_by):
        self.kilo = weight
        self.expiry = use_by
        Stock.__init__(self, stock_code, description, buy_price, mark_up)
        Warehouse.__init__(self, location, pack_type, qty_per_pack)

    def Expiring(self, discount):
        print('Price reduced for quick sale: ${}'.format(round(self.buy * self.margin * (1 - discount), 2)))

    def Label(self):
        print(self.desc, '\nWeight: ', self.kilo, 'kgs', '\nExpiry: ', self.expiry, '\nLocation: ', self.locn, '\nPacking: ', self.pack_type)

class Chicken(Meat): # Child class #1
    category = 'Chicken'

    def __init__(self, stock_code, description, buy_price, mark_up, location, pack_type, qty_per_pack, weight, use_by, portion, condition):
        self.cut = portion
        self.cond = condition
        Meat.__init__(self, stock_code, description, buy_price, mark_up, location, pack_type, qty_per_pack, weight, use_by)

class Pork(Meat): # Child class #2
     category = 'Pork'

     def __init__(self, stock_code, description, buy_price, mark_up, location, pack_type, qty_per_pack, weight, use_by, portion, cooking):
         self.cut = portion
         self.cooking = cooking
         Meat.__init__(self, stock_code, description, buy_price, mark_up, location, pack_type, qty_per_pack, weight, use_by)

class Beef(Meat): # Child class #3 
    category = 'Beef'

    def __init__(self, stock_code, description, buy_price, mark_up, location, pack_type, qty_per_pack, weight, use_by, portion, cooking):
        self.cut = portion
        self.cooking = cooking
        Meat.__init__(self, stock_code, description, buy_price, mark_up, location, pack_type, qty_per_pack, weight, use_by)


C793 = Chicken('C793', 'Chicken Pieces', 2.65, 1.756,  'F23A', 'Bag',  8, 0.495, '28 July 2021', 'Drumsticks', 'Frozen')

C864 = Pork('C864', 'Pork', 6.45, 1.367, 'F87C', 'Shrinkwrap',  1, 1.423, '2 July 2021', 'Leg', 'Roast')

C496 = Beef('C496', 'Beef', 4.53, 1.4768,  'F64B', 'Styrofoam Wrap', 1, 0.327, '4 July 2021', 'Steak', 'Braising')

# Do calls on stock labels

C793.Label()
C793.sell_price()
print()
C864.Label()
C864.sell_price()
print()
C496.Label()
C496.sell_price()

Result:

# Result

Chicken Pieces 
Weight:  0.495 kgs 
Expiry:  28 July 2021 
Location:  F23A 
Packing:  Bag
Retail price = $ 4.65

Pork 
Weight:  1.423 kgs 
Expiry:  2 July 2021 
Location:  F87C 
Packing:  Shrinkwrap
Retail price = $ 8.82

Beef 
Weight:  0.327 kgs 
Expiry:  4 July 2021 
Location:  F64B 
Packing:  Styrofoam Wrap
Retail price = $ 6.69

As you saw, the three children classes, Chicken, Pork, and Beef, all managed to access the Label method in the parent class, which accessed the attributes within both grandparent classes to identify the stock location and mark up attributes, and then directly accessed the sell_price method in the Stock class.

Summary

In this article, we recapped inheritance in Python, what it is, and why you’d use it. Then, we introduced the five different types of inheritance you might encounter in your programming before illustrating them with code examples.

Thank you for reading, and I hope you found the article helpful.