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.
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.
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.
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.
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.
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.