Class Composition vs Inheritance in Python
| technology | programming - Erik Mikac

Class Composition vs Inheritance in Python

To be a successful software developer, it is critical to have a firm understanding of class composition. In addition to class composition, class inheritance is equally important. In this post, we are going to define class composition and class inheritance — and look at examples of each. We'll also discuss the best situations to use each. Let's start with quick definitions of each.

What is Class Composition?

Class composition is when a class references another class in one of its fields. In other words, composition is a has-a relationship. As opposed to inheritance, which is an is-a relationship. We'll cover more about that shortly.

What is Class Inheritance?

Class inheritance is when a class is created as a subset of another class. The new class will have all the fields and methods of the class it inherited from — in addition to its own fields and methods. Inheritance is an is-a relationship.

Now that we have a quick and dirty definition of the two, let's walk through each of them. Both definitions are similar, but distinct differences exist. Let's start by discussing class composition in more detail.

Class Composition: A Deeper Look

It should be noted that class composition is not exclusive to Python: it's a fundamental building block of object-oriented programming. However it is widely used in Python, and any Python programmer should be familiar with the concept.

Remember that a class is created using methods and fields. Methods are procedures we would like a particular class to execute. For instance, if we had a class called Employee we would like a method called get_available_time_off to see how many days off they have left.

A field is an attribute associated with the class. In the employee example, a field would be something like "employee id" or "date of birth". I.E, some object or concept inherent to the data structure we are attempting to define. A field can be anything: a string, an integer, a decimal, or more importantly, another object. When a field is another object, then we have class composition. Below is a working example:

class Employee:
    def __init__(self, id, name, title):
        self.id = id
        self.name = name
        self.title = title
        self.benefits = Benefits(title)  # <—Composition Example
    def __str__(self):
        return “Employee(id=” + str(self.id) + “, name=” + self.name + “, title=” + self.title + “, Benefits=” +\
               self.benefits.__str__() + ")”

Notice that one of the attributes is called benefits. Remember, class composition is a has-a relationship. I.E, all employees have benefits. The benefits attribute is actually a class as well. Below is the class that makes up benefits:

class Benefits:
    amount_of_days_off = None
    has_company_car = None
    def __init__(self, title):
        self.title = title
        self.get_benefits()
    def get_benefits(self):
        if self.title == “Software Developer”:
            self.amount_of_days_off = 18
            self.has_company_car = False
        elif self.title == “HR”:
            self.amount_of_days_off = 20
            self.has_company_car = False
        elif self.title == “Manager”:
            self.amount_of_days_off = 25
            self.has_company_car = True
        elif self.title == “CEO”:
            self.amount_of_days_off = 30
            self.has_company_car = True
        else:
            raise Exception(“Title value must be one of the four shown above.”)
    def __str__(self):
        return “Benefits(amount_of_days_off=” + str(self.amount_of_days_off) + “, has_company_car= ” +\
               str(self.has_company_car) +”)”

As you can see, the employee class is a composition of its own fields and those in benefits. In the benefits class, we receive the title from the employee class. Based on the employee's title, we determine whether or not they will get a company car, and how many days they are apportioned. Notice that we are passing the title as a string. In a perfect world, this would be an enumeration instead.

Advantages of Composition

One of the biggest advantages of class composition is the flexibility it brings to your codebase. It allows you to loosely couple different fields so that they can be repurposed for different uses. That way, we can use the benefits class for other individuals. For example, if we wanted to create a contractor class or an hourly_employee class.

Another great advantage of composition is that the field can be changed at run time. This is something that cannot be done with inheritance. With class composition, if we wanted to change the benefits to something else during runtime, we easily can do so. Unlike inheritance, where the code would have to be rewritten. Speaking of Inheritance, let's dive into that a bit.

Class Inheritance: A Deeper Look

To turn to a different example, let's say you are the owner of a hardware store and need to represent your tool programmatically. Tools are all kind of similar after all, they all have a name, a description, and a price. In order to not reduplicate work, inheritance can be used here to make the code more organized.

First, we will create a class called tool. All different tools will inherit the properties of Tool. Remember, inheritance is an is-a relationship; a hammer is-a tool, a screwdriver is-a tool, etc. Here is an example of our tool class, which will be the superclass that all others will inherit from:

class Tool:
    def __init__(self, name, description, price):
        self.name = name
        self.description = description
        self.price = price
    def use(self): #<— use doesn't do anything unless it is part of an actual tool
        pass
    def __str__(self):
        return “Name: ” + self.name + “, description: ” + self.description + “, price: ” + self.price

It's only 10 or so lines of code, but it will really keep our code tight and organized. Because we're running a store, we know that a tool will have a price, a name, and a description. We also know that all tools are used for something, so a method called use has been included in the superclass. Additionally, our str magic method can be shared amongst all subclasses.

Now that we have defined the super class, let's create a couple of subclasses that will inherit these fields and the use method:

from Tool import Tool
class Screwdriver(Tool): #<— Here is screwdriver inheriting from Tool class.
    def __init__(self, screw_driver_type):
        super()
        self.screw_driver_type = screw_driver_type
        self.price = 3
    def use(self):  # <—Overriding the inherited method.
        print(“I’m using a ” + self.screw_driver_type + ” to screw in a screw!")

In this subclass, we created a class called screwdriver that inherits from the tool. The use function doesn't do anything unless we write something specific for the given subclass, so I wrote a simple print statement. Lastly, I set the price as an instance variable to three. This is saying that all screwdriver objects will be $3. Next, let's create a hammer class:

from Tool import Tool
class Hammer(Tool):
    def __init__(self):
        super()
        self.price = 5
    def use(self):
        print(“Hammer time! Hammer hammer…”)

Notice it is very similar to the screwdriver class. The main difference is that we customized the use method to be specific to a hammer. Finally, let's look at how these classes would be instantiated.

from Hammer import Hammer
from Screwdriver import Screwdriver
if __name__ == ‘__main__’:
    screwdriver = Screwdriver(“A tool for screwing in screws”, “Craftsman Screw Driver”, 3, “Phillips”)
    print(screwdriver)
    hammer = Hammer(“Kobalt Hammer”, “A tool used to hammer in nails”, 4)
    print(hammer)

After running this main class we would see:

—————————————————————————————————
Name: A tool for screwing in screws, description: Craftsman Screw Driver, price: 3
Name: Kobalt Hammer, description: A tool used to hammer in nails, price: 5
—————————————————————————————————

Advantages of Inheritance

The biggest advantage of inheritance is when you need a class that is very similar to another one, but it needs to be adjusted a little bit. Unlike class composition, this is creating a tight coupling between the parent class and the child class. Inheritance will help you adhere to one of the core principles of programming: DRY (Don't Repeat Yourself). By organizing your code intelligently using inheritance, it will be far easier to read and maintain.

Final Thoughts

Hopefully, you are beginning to understand differences between composition and inheritance. Inheritance gives us the ability to define broad characteristics for certain data structures, while composition allows us to insert classes into the fields of other classes.

The best way to truly understand the difference between the two is to practice, practice, practice. Continue to experiment with both until a firm understanding is established. Remember, if the relation is an is-a, then it is inheritance. If the relationship between the two concepts is a has-a, think class composition. Happy coding!

Download

Download

Ultimate DevOps Cert Guide

A 61-page guide to every DevOps, AWS, Azure, and Google Cloud certification, and how they fit into your career.

By submitting this form you agree that you have read, understood, and are able to consent to our privacy policy.

LEARNING ON MOBILE

Learn anytime anywhere with our mobile apps.

I have read and understood the privacy policy and am able to consent to it.

© 2021 CBT Nuggets. All rights reserved. Terms | Privacy Policy | Accessibility | Sitemap | 1550 Valley River Drive, Eugene, OR 97401 | 541-284-5522
CBT Nuggets