Skip to Content
Course content

119: Inheritance and the super() Function

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I once worked with a developer who spent three days tracking down a bug that only existed in one of twelve different payment processing classes. He had built a Payment class, and then created CreditCardPayment, PayPalPayment, ApplePayPayment, and so on. Instead of using inheritance properly, he had copy-pasted the core logic—things like transaction IDs and timestamping—into every single subclass. When a requirement changed regarding how we formatted transaction IDs, he updated the base class, but forgot about four of the subclasses. It was a nightmare to debug because the behavior was subtly inconsistent across the app.

That is exactly the kind of mess inheritance is designed to prevent. At its core, inheritance lets you define a "base" class with all the common behavior and then create "derived" classes that inherit those traits while adding their own specific twists. You aren't just saving keystrokes; you're creating a single source of truth for your logic.

Stopping the Copy-Paste Cycle with Inheritance

When you create a child class, you pass the parent class as an argument in the class definition. From that moment on, the child class has access to every method and attribute of the parent. You don't have to redefine the things that are universal to all objects of that type.

class Payment:
    def __init__(self, amount, currency):
        self.amount = amount
        self.currency = currency
        print(f"Processing {self.amount} {self.currency}...")

    def generate_receipt(self):
        print(f"Receipt created for {self.amount} {self.currency}")

class CreditCardPayment(Payment):
    def authorize_card(self):
        print("Checking credit limit and CVV...")

# CreditCardPayment now has access to generate_receipt() 
# even though we didn't define it inside the child class.
cc_pay = CreditCardPayment(100, "USD")
cc_pay.generate_receipt()

In this example, CreditCardPayment is the child. It gets everything Payment has for free, but it also gets its own specialized method, authorize_card(). This keeps your code clean and ensures that if you ever need to change how generate_receipt works, you only have to change it in one place.

Using super() to Avoid Redundant Setup

Now, here is where things usually get tricky for people. What happens if your child class needs its own __init__ method to handle extra data? If you define __init__ in the child class, it completely overrides the parent's __init__. You'll find that your amount and currency attributes suddenly vanish because the parent's setup code never ran.

This is why we use the super() function. It tells Python, "Go back to the parent class and run this method first, then come back here and finish what I'm doing." It's essentially a way to extend the parent's behavior rather than replacing it entirely. I've seen far too many developers try to call the parent class by name (like Payment.__init__(self, ...)), but super() is the professional standard because it handles complex inheritance hierarchies much more gracefully.

class CreditCardPayment(Payment):
    def __init__(self, amount, currency, card_number):
        # We call the parent's __init__ to handle amount and currency
        super().__init__(amount, currency) 
        # Now we handle the part specific to credit cards
        self.card_number = card_number
        print(f"Card {self.card_number} linked to transaction.")

cc_pay = CreditCardPayment(150, "EUR", "4111-XXXX-XXXX-1111")
# Because of super(), we still have access to the amount attribute
print(f"Total: {cc_pay.amount}")

Without that super().__init__(amount, currency) call, cc_pay.amount would throw an AttributeError. You're essentially layering your logic: the base class handles the "what is a payment?" part, and the child class handles the "what makes this a credit card payment?" part.




📋 Practical Task

Building a Specialized Vehicle Fleet Management System

You are building a system to track a company's vehicle fleet. You need to ensure that all vehicles share some basic traits, but different types of vehicles have different requirements.

  • Create a base class called Vehicle. Its __init__ method should accept and assign make and model. It should also have a method called get_description that returns a string like "This is a [make] [model]".
  • Create a derived class called ElectricVehicle. This class should inherit from Vehicle.
  • The ElectricVehicle class needs its own __init__ method that accepts make, model, and battery_capacity. Use super() to handle the make and model, and assign the battery capacity locally.
  • Override the get_description method in ElectricVehicle so that it returns the original description from the parent class PLUS the battery capacity (e.g., "This is a Tesla Model 3 with a 75kWh battery"). Use super().get_description() inside this method to avoid rewriting the string logic.

Test your code by: Creating an instance of ElectricVehicle and printing the result of get_description().

Rating
0 0

There are no comments for now.

to be the first to leave a comment.