ADVERTISEMENT
Python · Polymorphism · Inheritance · El Nido

El Nido Business Tracker

Python OOP · Polymorphism & Method Overriding Guide

A complete walkthrough of polymorphism, method overriding, and super() — explained step by step through a real business profit tracker for Disco Hub and Shawarma Store in El Nido, Palawan. Every class, every method, every design decision explained.

🧱 1 Base + 2 Subclasses 🔄 Polymorphism 🔧 Method Overriding 📊 Profit Reports 🔁 10 Sections 🐍 Python 3
SCROLL TO EXPLORE
INTRODUCTION

What Is El Nido Business Tracker and Why Does It Matter?

Imagine you own two very different businesses in the bustling tourist town of El Nido, Palawan: a high‑energy disco hub and a cozy shawarma store. Both generate daily sales, incur daily expenses, and pay workers’ salaries. But each has unique costs — the disco pays a DJ fee, the shawarma shop loses money to ingredient waste. How can you model these businesses in Python without duplicating code and while keeping the ability to treat them uniformly?

The answer is polymorphism through inheritance and method overriding. The BusinessUnit base class captures everything common to both businesses: sales, expenses, salaries, and basic profit calculations. Then DiscoHub and ShawarmaStore inherit from it, overriding only the methods that need to account for their unique costs. Thanks to polymorphism, you can store both types in a single list and call the same methods (generate_report()) — Python automatically runs the correct version for each object.

This guide walks through every line of the program, explaining not just what the code does, but why each design choice was made — from using super() to extend parent logic, to the importance of keeping method signatures identical for polymorphism.

🐍
What you will learn from this guide: Polymorphism in action, method overriding, proper use of super(), the DRY principle, designing extensible class hierarchies, and a clean command‑line report generator — all in one concise, real‑world program.
💡
Who this guide is for: Python learners comfortable with basic classes and ready to understand how polymorphism makes code flexible and maintainable. No external libraries — only core Python.
ADVERTISEMENT
SECTION 01

Program Overview

The program consists of one base class and two subclasses. The flow is simple: create instances, store them in a list, loop over the list, and call generate_report() on each. The magic of polymorphism ensures each object’s overridden methods are used automatically.

Class 1 — BusinessUnit
The base class. Holds name, daily_sales, daily_expenses, workers_salary. Provides methods to calculate net expenses, gross profit, net profit, and a formatted report.
Class 2 — DiscoHub
Inherits from BusinessUnit. Adds dj_entertainment_fee. Overrides calculate_net_expenses() to include this fee using super().
Class 3 — ShawarmaStore
Inherits from BusinessUnit. Adds ingredient_waste. Overrides calculate_gross_profit() to subtract waste from the base gross profit.
🏗️Base Class
BusinessUnit defines common structure
🎧DiscoHub
Adds DJ fee, overrides net expenses
🥙ShawarmaStore
Adds waste, overrides gross profit
🔄Polymorphic Loop
for b in businesses: b.generate_report()
SECTION 02

No Imports Needed — Pure Python

disco.py — no imports
# This program uses only built-in Python features.
# No import statements are needed.

Unlike many real‑world programs that rely on external modules, this example stays completely self‑contained. Everything you need — classes, methods, string formatting, and the if __name__ == "__main__" guard — is part of Python’s core. This keeps the focus on OOP concepts without distractions.

SECTION 03

The BusinessUnit Base Class

class BusinessUnit
class BusinessUnit:
    def __init__(self, name: str, daily_sales: float, daily_expenses: float, workers_salary: float):
        self.name = name
        self.daily_sales = daily_sales
        self.daily_expenses = daily_expenses
        self.workers_salary = workers_salary

    def calculate_net_expenses(self) -> float:
        return self.daily_expenses + self.workers_salary

    def calculate_gross_profit(self) -> float:
        return self.daily_sales - self.daily_expenses

    def calculate_net_profit(self) -> float:
        return self.daily_sales - self.calculate_net_expenses()

    def generate_report(self):
        print(f"\n{'='*60}")
        print(f"EL NIDO BUSINESS TRACKER - {self.name}")
        print(f"{'='*60}")
        print(f"Daily Sales          : PHP {self.daily_sales:,.2f}")
        print(f"Daily Expenses       : PHP {self.daily_expenses:,.2f}")
        print(f"Workers' Salary      : PHP {self.workers_salary:,.2f}")
        print(f"Net Expenses         : PHP {self.calculate_net_expenses():,.2f}")
        print(f"Gross Profit         : PHP {self.calculate_gross_profit():,.2f}")
        print(f"Net Profit           : PHP {self.calculate_net_profit():,.2f}")
        print(f"{'='*60}\n")

The BusinessUnit class is the foundation. It defines four attributes that every business shares and four methods that calculate various profit metrics. The type hints (name: str, etc.) serve as documentation; Python does not enforce them at runtime. The generate_report() method prints a well‑formatted summary, using f‑strings with the :,.2f format specifier to display numbers with commas and two decimal places — essential for readability when dealing with Philippine Peso amounts.

Notice that calculate_net_profit() calls self.calculate_net_expenses() rather than repeating the formula. This is a deliberate design: it ensures that any subclass that overrides calculate_net_expenses() will automatically affect the net profit calculation. This is the key to polymorphism — the base method uses a hook that child classes can customise.

SECTION 04

DiscoHub — Extending with super()

class DiscoHub(BusinessUnit)
class DiscoHub(BusinessUnit):
    def __init__(self, name: str, daily_sales: float, daily_expenses: float,
                 workers_salary: float, dj_entertainment_fee: float):
        super().__init__(name, daily_sales, daily_expenses, workers_salary)
        self.dj_entertainment_fee = dj_entertainment_fee

    def calculate_net_expenses(self) -> float:
        base_net = super().calculate_net_expenses()
        return base_net + self.dj_entertainment_fee

A disco hub has an extra cost that a generic business does not: the DJ entertainment fee. The constructor takes this additional parameter, then calls the parent constructor using super().__init__(...) to initialise the common attributes. This avoids duplicating the four lines of attribute assignment from BusinessUnit.

The overridden calculate_net_expenses() first retrieves the base net expenses by calling super().calculate_net_expenses() — which returns daily_expenses + workers_salary — and then adds the DJ fee. This is the canonical way to extend a parent method: reuse the parent logic and then augment it. The method signature (no parameters, returns float) is identical to the parent’s, satisfying the Liskov substitution principle.

SECTION 05

ShawarmaStore — Overriding Gross Profit

class ShawarmaStore(BusinessUnit)
class ShawarmaStore(BusinessUnit):
    def __init__(self, name: str, daily_sales: float, daily_expenses: float,
                 workers_salary: float, ingredient_waste: float):
        super().__init__(name, daily_sales, daily_expenses, workers_salary)
        self.ingredient_waste = ingredient_waste

    def calculate_gross_profit(self) -> float:
        return self.daily_sales - self.daily_expenses - self.ingredient_waste

The shawarma store introduces a different kind of unique cost: ingredient waste. Unlike the disco’s fee, waste directly reduces the gross profit rather than increasing expenses. Therefore, the class overrides calculate_gross_profit() instead of calculate_net_expenses(). It does not call super() because it completely replaces the parent’s logic — the parent formula (daily_sales - daily_expenses) is no longer sufficient.

Notice that the method signature remains the same: no parameters, returns a float. This consistency is vital for polymorphism. Also, the overridden version still uses the parent’s attributes (daily_sales, daily_expenses) because they are inherited. The new attribute ingredient_waste is used only here.

SECTION 06

The main Block — Polymorphism in Action

if __name__ == "__main__":
if __name__ == "__main__":
    print("🚀 El Nido Business Tracker - Disco Hub & Shawarma Store")
    print("Demonstrating Polymorphism in Python\n")

    disco = DiscoHub(
        name="Vibe Palace Disco Hub",
        daily_sales=85000,
        daily_expenses=32000,
        workers_salary=15000,
        dj_entertainment_fee=8000
    )

    shawarma = ShawarmaStore(
        name="El Nido Shawarma Hub",
        daily_sales=42000,
        daily_expenses=18000,
        workers_salary=9000,
        ingredient_waste=2500
    )

    businesses = [disco, shawarma]

    for business in businesses:
        business.generate_report()

This is where everything comes together. Two objects of different types are created and placed in a single list. The loop iterates over the list and calls generate_report() on each. Thanks to polymorphism, Python looks up the method at runtime and executes the version belonging to the object’s actual class.

For the disco object, generate_report() (inherited from BusinessUnit) calls calculate_net_expenses(), which is the overridden version in DiscoHub that includes the DJ fee. For the shawarma object, calculate_gross_profit() called from within generate_report() uses the overridden version that subtracts ingredient waste. The same report method produces correct, business‑specific results without any if statements or type checks.

SECTION 07

Polymorphism Deep Dive

Polymorphism (from Greek “many forms”) in Python means that the same method call can trigger different implementations depending on the object’s type. It is one of the four fundamental pillars of OOP (along with encapsulation, inheritance, and abstraction). In this program, polymorphism is achieved through inheritance and method overriding.

How Python decides which method to call

When you write business.generate_report(), Python:

  1. Looks at the class of business (e.g., DiscoHub).
  2. Searches for generate_report in that class. Not found.
  3. Searches in the parent class (BusinessUnit). Found — so it calls BusinessUnit.generate_report(business).
  4. Inside that method, when self.calculate_net_expenses() is called, Python again looks at the actual class of self (which is still DiscoHub) and finds the overridden version. That version runs.

This dynamic dispatch happens at runtime, not compile time. It makes your code flexible: you can add new business types later, and the existing loop will handle them automatically as long as they follow the same interface (i.e., they have a generate_report method).

📌
Liskov Substitution Principle: Any subclass must be substitutable for its parent without breaking the program. Here, both DiscoHub and ShawarmaStore can be used anywhere a BusinessUnit is expected — they don’t add new required methods and they don’t change the meaning of existing methods in a way that would surprise the caller.
SECTION 08

Method Overriding & super()

Overriding a method means providing a new implementation in a child class that replaces the parent’s version. The child can either completely replace the parent logic (like ShawarmaStore.calculate_gross_profit) or extend it (like DiscoHub.calculate_net_expenses).

When to use super()

super() gives you access to the parent class’s methods. Use it when you want to reuse the parent’s logic as part of your overridden method. In DiscoHub, calling super().calculate_net_expenses() avoids recalculating the base net expenses. This keeps your code DRY (Don’t Repeat Yourself) and ensures that if the base formula ever changes (e.g., adding a new type of expense), the child automatically inherits the update.

In contrast, ShawarmaStore does not call super().calculate_gross_profit() because the parent’s formula is not part of the correct result — waste is subtracted, not added. Complete replacement is the right choice here.

What if you forget to call super() in __init__?

If you omit super().__init__() in DiscoHub.__init__, the attributes name, daily_sales, etc., would never be set on the object. Accessing them later would raise an AttributeError. Always call the parent constructor when you override __init__, unless you have a very specific reason not to.

SECTION 09

Common Mistakes & Best Practices

Forgetting to call the parent constructor

Always call super().__init__(...) in the child’s __init__ to initialise inherited attributes.

Changing the method signature when overriding

The overridden method must accept the same parameters (and return a compatible type) as the parent. Otherwise, code that expects the parent’s signature will break.

Keep the base class focused

BusinessUnit contains only what is common to all businesses. Unique costs are pushed down to subclasses. This is the Single Responsibility Principle.

Use super() to extend, not replace

When your override needs the parent’s result, call super().method() and then augment it. This avoids duplication and centralises logic.

SECTION 10

Try It Yourself — Extension Challenges

Solidify your understanding by extending the program:

🟢 Beginner — Add a third business
Create a DiveShop class that inherits from BusinessUnit and adds an equipment_rental_cost. Override calculate_net_expenses to include this cost. Instantiate it and add to the list.
🟡 Intermediate — Add a discount system
Create a Discountable mixin with a discount_percent attribute and a apply_discount(amount) method. Modify ShawarmaStore to also inherit from this mixin and apply a 10% discount to gross profit for loyalty program.
🔴 Advanced — Load data from JSON
Write a function that reads business data from a businesses.json file and dynamically creates the appropriate objects (using a factory pattern). Save reports to separate text files.
SECTION 11

Complete Source Code

disco.py — Full Source
class BusinessUnit:
    def __init__(self, name: str, daily_sales: float, daily_expenses: float, workers_salary: float):
        self.name = name
        self.daily_sales = daily_sales
        self.daily_expenses = daily_expenses
        self.workers_salary = workers_salary

    def calculate_net_expenses(self) -> float:
        return self.daily_expenses + self.workers_salary

    def calculate_gross_profit(self) -> float:
        return self.daily_sales - self.daily_expenses

    def calculate_net_profit(self) -> float:
        return self.daily_sales - self.calculate_net_expenses()

    def generate_report(self):
        print(f"\n{'='*60}")
        print(f"EL NIDO BUSINESS TRACKER - {self.name}")
        print(f"{'='*60}")
        print(f"Daily Sales          : PHP {self.daily_sales:,.2f}")
        print(f"Daily Expenses       : PHP {self.daily_expenses:,.2f}")
        print(f"Workers' Salary      : PHP {self.workers_salary:,.2f}")
        print(f"Net Expenses         : PHP {self.calculate_net_expenses():,.2f}")
        print(f"Gross Profit         : PHP {self.calculate_gross_profit():,.2f}")
        print(f"Net Profit           : PHP {self.calculate_net_profit():,.2f}")
        print(f"{'='*60}\n")


class DiscoHub(BusinessUnit):
    def __init__(self, name: str, daily_sales: float, daily_expenses: float,
                 workers_salary: float, dj_entertainment_fee: float):
        super().__init__(name, daily_sales, daily_expenses, workers_salary)
        self.dj_entertainment_fee = dj_entertainment_fee

    def calculate_net_expenses(self) -> float:
        base_net = super().calculate_net_expenses()
        return base_net + self.dj_entertainment_fee


class ShawarmaStore(BusinessUnit):
    def __init__(self, name: str, daily_sales: float, daily_expenses: float,
                 workers_salary: float, ingredient_waste: float):
        super().__init__(name, daily_sales, daily_expenses, workers_salary)
        self.ingredient_waste = ingredient_waste

    def calculate_gross_profit(self) -> float:
        return self.daily_sales - self.daily_expenses - self.ingredient_waste


if __name__ == "__main__":
    print("🚀 El Nido Business Tracker - Disco Hub & Shawarma Store")
    print("Demonstrating Polymorphism in Python\n")

    disco = DiscoHub(
        name="Vibe Palace Disco Hub",
        daily_sales=85000,
        daily_expenses=32000,
        workers_salary=15000,
        dj_entertainment_fee=8000
    )

    shawarma = ShawarmaStore(
        name="El Nido Shawarma Hub",
        daily_sales=42000,
        daily_expenses=18000,
        workers_salary=9000,
        ingredient_waste=2500
    )

    businesses = [disco, shawarma]

    for business in businesses:
        business.generate_report()
Was this helpful?
Share This Guide
ADVERTISEMENT
FAQ

Frequently Asked Questions

El Nido Business Tracker is a Python command-line program that models two businesses: a Disco Hub and a Shawarma Store. It demonstrates polymorphism by having a base class BusinessUnit and two subclasses that override specific methods to account for unique costs (DJ fee, ingredient waste). A single loop calls the same methods on both objects, and the correct versions run automatically.
Because the nature of the unique cost differs. The DJ fee is an extra expense, so it naturally belongs in calculate_net_expenses. Ingredient waste directly reduces the revenue after expenses, so it fits better in calculate_gross_profit. Overriding the appropriate method keeps the class design intuitive and follows the principle of least surprise.
Technically yes, but that would violate the Open/Closed Principle and lead to messy code full of if statements. Polymorphism allows you to add new business types without modifying existing code—just create a new subclass and it works. Flags would require updating every conditional.
Save the code as disco.py and run python disco.py from your terminal. You will see the formatted reports for both businesses printed to the console.
COMMUNITY

Comments & Discussion

💬 Leave a Comment

💡 Comments are stored locally in this session. Be respectful and constructive.

J
Joanna Cruz
March 2026
The super() explanation finally clicked! I used to just copy-paste parent code, but now I see how to properly extend methods. The DJ fee example was perfect. Salamat!
M
Marco Lopez
March 2026
I added a DiveShop class with equipment rental cost and it worked instantly with the existing loop. Polymorphism is magic! Great tutorial.
CONNECT

Follow Glenn Junsay Pansensoy

Stay connected for more Python guides, OOP tutorials, and tech content from the Philippines. Follow on your preferred platform:

ADVERTISEMENT
🎓 You've Mastered Polymorphism — Now Build Something!

You have walked through every line of El Nido Business Tracker, from the base class to the polymorphic loop. You now understand how method overriding, super(), and runtime dispatch work together to create flexible, maintainable code.

Polymorphism is everywhere in Python — from built‑in functions like len() to frameworks like Django. The same principles you learned here will help you design systems that are easy to extend and debug.

Your next step? Take the extension challenges seriously. Add a DiveShop, implement a discount mixin, or load data from JSON. Each challenge will deepen your understanding.

🔁 Read Again 💻 Copy Source
🤖 AI-Powered Social Share

Let AI craft the perfect caption for sharing this article. Pick your platform and tone, then generate a ready-to-post message tailored for your audience.

AI is crafting your caption…
📘 🐦 💼