HOLIDAY SALE! Save 50% on Membership with code HOLIDAY50. Save 15% on Mentorship with code HOLIDAY15.

3) Inheritance Lesson

Customize Python __init__

8 min to complete · By Martin Breuss

Up to now, you've only added or replaced complete methods in your child class. But what if you wanted to record the taste of each and every Spice() in addition to its name and amount?

A good place to set values that every instance of a class should have is right at the time of instantiating the object. You wanted every Ingredient() to have a name and an amount, so you recorded it in your __init__() method:

class Ingredient:
    """Models an Ingredient."""

    def __init__(self, name, amount):
        self.name = name
        self.amount = amount

Init Override

You defined the required parameters in the __init__() method. And because dunder methods are like any other method, you can override them, too. You could replace the whole __init__() method in your Spice() child class with a completely new one and define all the parameters and variable assignments again. This is what you did with the .expire() method:

class Spice(Ingredient):
    """Models a spice to flavor your food."""

    def __init__(self, name, amount, taste):
        self.name = name
        self.amount = amount
        self.taste = taste

Constructor Import with Super Method

However, with longer constructor methods that might even have some logic involved, this can get cumbersome and cause more work and copied code than necessary. Remember, whenever possible, you want to follow the DRY rule, which stands for Don't Repeat Yourself.

You want to keep name and amount just as they are in your Ingredient() class, but you want to add an additional taste variable. Another way of doing this is to call the constructor method of your parent class in the constructor method of your child class:

class Spice(Ingredient):
    """Models a spice to flavor your food."""

    def __init__(self, name, amount, taste):
        super().__init__(name, amount)
        self.taste = taste

The call to super() gives you access to the contents of your parent class. And just like you would with any other method, you then explicitly call the .__init__() constructor of the parent class, passing it the two arguments that it requires.

Colorful illustration of a light bulb

Info: You can access any method and attribute of the parent class in your child class through super(). However, most commonly, you'll see this construct in the __init__() method, as shown above.

This will set up the name and amount attributes just as defined in the dunder init method of your parent class, Ingredient(). Finally, you add one additional line of code that assigns the additional taste parameter to the instance of your Spice() class through the self variable.

Break Down the Code

The Spice class constructor above overrides the parent class constructor but also leverages its functionality by calling it through super():

  • In the first line, you define all parameters that you eventually need. All parameters that you define here will need to be passed as arguments to the constructor when creating a new instance of the class.
  • In the second line, you're making a call to the __init__() method of your parent class. You're passing the two required arguments name and amount, which allows it to work as it was defined in Ingredient().
  • In the third line, you assign the added taste parameter to the object, just like you did with the other variables defined in the dunder init method of the parent class.

Every Spice() object that you create now requires an additional argument, taste, that Ingredient() objects don't take.

Instantiate the Objects

Instantiating an object of each class now works differently between Ingredient() and Spice() objects:

c = Ingredient("carrots", 2)
p = Spice("pepper", 20, "hot")

You can also access the .taste attribute of a Spice() object, while this doesn't exist for an Ingredient() object.

Illustration of a lighthouse

Tasks

  • Try to instantiate a Spice() object without passing a third argument. What happens?
  • Try to instantiate an Ingredient() object with a third argument. What error message does Python show you?
  • Confirm that you can access .taste on your Spice() object.
  • Try to access .taste on your Ingredient() object. What error message do you get?

Keep playing around with the code snippets and keep trying to break things and encounter new error messages. Python is very forgiving and communicative, and it'll always try to get you back on track. It's incredibly important to become familiar with reading these error messages, and you can learn a lot about the language when doing it, too.

Summary: Customize Python Init

  • You can add additional instance variables to a child class by modifying the __init__() method
  • You can leverage functionality defined in the parent class through super()
  • The child class constructor overrides the parent class constructor but can also leverage its functionality by calling it through super()
  • Class inheritance combined with overriding and extending child classes gives you a powerful tool to model your custom objects in Python.

Syntax

    def __init__(self, name, amount, taste):
        super().__init__(name, amount)
        self.taste = taste