ADVERTISEMENT
Python · Inheritance · Polymorphism · El Nido

Pasta & Pizza Hub

Python OOP · Inheritance & Polymorphism Guide

A complete walkthrough of inheritance, polymorphism, method overriding, and super() — explained step by step through a real Italian restaurant ordering system in El Nido, Palawan. Every class, every method, every design decision explained.

🧱 1 Base + 2 Subclasses 🍕 20 Pizzas 🍝 20 Pastas 🔄 Polymorphism 🔧 Method Overriding 🐍 Python 3
SCROLL TO EXPLORE
INTRODUCTION

What Is Pasta & Pizza Hub and Why Does It Matter?

Welcome to Pasta & Pizza Hub – a Python command-line program that simulates a bustling Italian restaurant in the heart of El Nido, Palawan. Customers can choose from 20 different pizzas and 20 pastas, place an order, see a detailed summary, and watch as the kitchen prepares each item with a unique message. Behind this simple user experience lies a powerful demonstration of object-oriented programming: inheritance and polymorphism.

The program defines a base class Product that holds a name and price, and provides generic methods get_info() and process_order(). Two subclasses, Pizza and Pasta, inherit from Product. Each adds its own attribute (crust_type for pizza, sauce_type for pasta) and overrides the two methods to give them a unique flavour. When the final order is processed, a simple loop calls process_order() on every item – and Python automatically runs the correct version (baking message 🍕 for pizzas, boiling message 🍝 for pastas) thanks to polymorphism.

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() in constructors, to storing products in a dictionary with clever codes, to validating user input.

🐍
What you will learn from this guide: Inheritance and polymorphism in action, method overriding, proper use of super(), the DRY principle, dictionary‑based product lookup, input validation with try/except, and a realistic order flow – all in one concise, real‑world program.
💡
Who this guide is for: Python learners comfortable with basic classes and ready to understand how inheritance and polymorphism make code reusable and flexible. No external libraries – only core Python.
ADVERTISEMENT
SECTION 01

Program Overview

The program consists of one base class (Product) and two subclasses (Pizza and Pasta). It creates 20 pizzas and 20 pastas with realistic names and prices, stores them in a dictionary using codes PIZ01 to PIZ20 and PAS01 to PAS20, displays the full menu, lets the user add items to a cart, calculates totals, collects payment details, and finally shows polymorphic preparation messages.

Class 1 — Product
Base class with name, price, calculate_total(), get_info(), and process_order(). Serves as the blueprint for any sellable item.
Class 2 — Pizza
Inherits from Product. Adds crust_type (default "Thin"). Overrides get_info() to include crust info and process_order() to print a baking message.
Class 3 — Pasta
Inherits from Product. Adds sauce_type (default "Tomato"). Overrides get_info() to include sauce info and process_order() to print a boiling message.
📋Menu Creation
20 pizzas + 20 pastas → dictionary with codes
🛒Cart Input
Enter codes, quantities, validate
💰Payment
Name, phone, cash/credit, confirmation
🍕🍝Polymorphic Prep
item.process_order(qty) → baking 🍕 or boiling 🍝
SECTION 02

import random — Only External Module

pizza.py — import random
import random

The only module imported is random, which is part of Python’s standard library. It is used at the very end to generate a random 6‑digit order confirmation code (random.randint(100000, 999999)). This adds a realistic touch – after payment, the customer receives a unique code to show at pickup. Everything else (classes, input handling, loops) uses pure Python.

SECTION 03

The Product Base Class

class Product
class Product:
    def __init__(self, name: str, price: float):
        self.name = name
        self.price = price

    def calculate_total(self, quantity: int) -> float:
        return self.price * quantity

    def get_info(self) -> str:
        return f"{self.name} - PHP {self.price:.2f}"

    def process_order(self, quantity: int):
        print(f"Preparing {quantity} x {self.name}...")

Product is the foundation. It defines two attributes every product must have: a name and a price. The method calculate_total() returns the price multiplied by quantity – a simple but essential formula. get_info() returns a formatted string for display, and process_order() prints a generic preparation message. This class is intentionally kept minimal; all specialised behaviour will be added by subclasses.

Type hints (name: str, price: float) are optional but serve as built‑in documentation. The f"{self.price:.2f}" format ensures prices always show two decimal places.

SECTION 04

Pizza — Adding Crust and Overriding Methods

class Pizza(Product)
class Pizza(Product):
    def __init__(self, name: str, price: float, crust_type: str = "Thin"):
        super().__init__(name, price)
        self.crust_type = crust_type

    def get_info(self) -> str:
        return f"Pizza: {self.name} ({self.crust_type} Crust) - PHP {self.price:.2f}"

    def process_order(self, quantity: int):
        print(f"Baking {quantity} x {self.name} pizza... 🍕")

The Pizza class introduces a new attribute, crust_type, with a default value of "Thin". The constructor uses super().__init__(name, price) to delegate the initialisation of the inherited attributes to the parent class. This is a clean application of the DRY principle – we don’t repeat the assignment of self.name and self.price.

Both get_info() and process_order() are overridden. get_info() now includes the crust type in the description, making the menu more informative. process_order() prints a playful baking message with a pizza emoji. The method signatures (parameters and return types) remain identical to the parent’s – this is crucial for polymorphism.

SECTION 05

Pasta — Adding Sauce and Overriding Methods

class Pasta(Product)
class Pasta(Product):
    def __init__(self, name: str, price: float, sauce_type: str = "Tomato"):
        super().__init__(name, price)
        self.sauce_type = sauce_type

    def get_info(self) -> str:
        return f"Pasta: {self.name} ({self.sauce_type} Sauce) - PHP {self.price:.2f}"

    def process_order(self, quantity: int):
        print(f"Boiling {quantity} x {self.name} pasta... 🍝")

The Pasta class mirrors Pizza but with a different unique attribute: sauce_type. Again, the parent constructor is called via super(). The overridden get_info() mentions the sauce, and process_order() prints a boiling message with a pasta emoji.

This symmetry is intentional – it shows how two different subclasses can each extend the base class in their own way while still conforming to the same interface. This is the essence of polymorphism.

SECTION 06

Building the Full Menu (20 Pizzas + 20 Pastas)

Menu creation
pizza_names = ["Margherita", "Pepperoni", ...]  # 20 names
pizza_prices = [220, 280, ...]                   # 20 prices

pasta_names = ["Spaghetti Bolognese", ...]       # 20 names
pasta_prices = [180, 210, ...]                   # 20 prices

products = {}
for i in range(20):
    code = f"PIZ{i+1:02d}"
    products[code] = Pizza(pizza_names[i], pizza_prices[i])
for i in range(20):
    code = f"PAS{i+1:02d}"
    products[code] = Pasta(pasta_names[i], pasta_prices[i])

Two lists hold the names and prices of 20 pizzas and 20 pastas. A dictionary named products maps human‑readable codes (like PIZ05) to the actual product objects. Using a dictionary gives O(1) lookup – when the user enters a code, we can instantly retrieve the corresponding object.

The f"PIZ{i+1:02d}" format creates codes with leading zeros, e.g., PIZ01, PIZ02, …, PIZ20. This ensures consistent code length and makes the menu look professional. The same logic is applied to pastas with the prefix PAS.

🗂️
Why a dictionary? If we used a list and searched by code every time, the lookup would be O(n). With a dictionary, it’s constant time. This is a small optimisation, but it also makes the code cleaner: we can directly do if choice in products.
SECTION 07

The Order Flow — Cart, Payment, and Confirmation

The main program displays the menu (by iterating over the dictionary keys in order), then enters a loop where the user can type product codes. The loop continues until the user types done.

Cart input loop
cart = []
while True:
    choice = input("\nProduct code: ").strip().upper()
    if choice == "DONE": break
    if choice in products:
        try:
            qty = int(input(f"Quantity for {choice}: "))
            if qty > 0:
                cart.append((products[choice], qty))
                print(f"✅ Added {qty} × {products[choice].name}")
            else:
                print("Quantity must be positive.")
        except ValueError:
            print("Invalid quantity.")
    else:
        print("❌ Invalid code. Please check the menu.")

The input is stripped and converted to uppercase so that piz05 works just like PIZ05. The code is checked against the dictionary keys. If valid, the program asks for a quantity, validates it with a try/except to catch non‑numeric input, and ensures the quantity is positive. The product object and quantity are stored as a tuple in the cart list.

After ordering, the cart is displayed with itemised totals, the grand total is calculated, and the user enters their name, phone, and payment method. A simple confirmation step follows; if confirmed, a random order code is generated and the polymorphic order processing begins.

Polymorphic preparation
print("\n🔧 Preparing your order now:")
for item, qty in cart:
    item.process_order(qty)

This loop is the heart of polymorphism. Even though cart contains both Pizza and Pasta objects, calling process_order() on each triggers the correct overridden method – baking 🍕 for pizzas, boiling 🍝 for pastas. No if statements, no type checking – just clean, polymorphic dispatch.

SECTION 08

Polymorphism Deep Dive

Polymorphism (from Greek “many forms”) means that the same method call can behave differently depending on the object’s actual class. In Python, this is achieved through inheritance and method overriding.

How Python decides which process_order to run

When the loop calls item.process_order(qty), Python:

  1. Looks at the class of item (e.g., Pizza).
  2. Searches for process_order in that class. Found – so it calls Pizza.process_order(item, qty).
  3. If the method were not found in Pizza, Python would look in the parent class Product.

This dynamic dispatch happens at runtime, giving you tremendous flexibility. You can add a new subclass (e.g., Salad) with its own process_order, and the loop will handle it without any modifications – that’s the open/closed principle in action.

📌
Liskov Substitution Principle: Any subclass must be usable wherever its parent is expected. Here, both Pizza and Pasta can be placed in the cart and passed to any function that expects a Product – they don’t break the program.
SECTION 09

Method Overriding & super()

Overriding a method means providing a new implementation in a child class that replaces the parent’s version. In this program, both get_info() and process_order() are overridden.

When to use super()

super() is used in the constructor to call the parent’s __init__ and initialise inherited attributes. This avoids repeating the assignment of self.name and self.price. If you forget super().__init__, those attributes will never be set, leading to an AttributeError later.

In the overridden methods themselves, we don’t call super() because we are completely replacing the parent’s behaviour. If we wanted to extend the parent method (e.g., print the generic message and an emoji), we could call super().process_order(quantity) first.

SECTION 10

Common Mistakes & Best Practices

Forgetting to call super().__init__

If you omit the parent constructor call, the base attributes (name, price) are never set. Always call super().__init__(...) when you override the constructor.

Changing the method signature when overriding

process_order in Pizza must accept the same parameters (self, quantity) as in Product. If you add an extra parameter, code that calls the method polymorphically will break.

Use dictionaries for fast lookup

Mapping codes to objects with a dictionary is efficient and clean. It also allows simple membership testing with if code in products.

Validate user input with try/except

Wrapping int(input()) in a try block catches non‑numeric entries gracefully. Always provide a helpful error message and continue the loop.

Use .strip() and .upper() on input

This makes the program more forgiving: " piz05 " becomes "PIZ05" and matches the dictionary key.

SECTION 11

Try It Yourself — Extension Challenges

Solidify your understanding by extending the program:

🟢 Beginner — Add a third category
Create a Salad class that inherits from Product and adds a dressing attribute. Override get_info() and process_order() (e.g., "Tossing salad... 🥗"). Add 10 salads to the menu with codes SAL01 to SAL10.
🟡 Intermediate — Discount for large orders
If the total quantity exceeds a threshold (e.g., 10 items), apply a 10% discount. Modify the order summary to show the discount and new total.
🔴 Advanced — Save order history
Write each completed order to a JSON file named order_.json. Include customer details, items with quantities, and total. Use the json module with ensure_ascii=False to preserve emojis.
SECTION 12

Complete Source Code

pizza.py — Full Source
import random

class Product:
    def __init__(self, name: str, price: float):
        self.name = name
        self.price = price

    def calculate_total(self, quantity: int) -> float:
        return self.price * quantity

    def get_info(self) -> str:
        return f"{self.name} - PHP {self.price:.2f}"

    def process_order(self, quantity: int):
        print(f"Preparing {quantity} x {self.name}...")


class Pizza(Product):
    def __init__(self, name: str, price: float, crust_type: str = "Thin"):
        super().__init__(name, price)
        self.crust_type = crust_type

    def get_info(self) -> str:
        return f"Pizza: {self.name} ({self.crust_type} Crust) - PHP {self.price:.2f}"

    def process_order(self, quantity: int):
        print(f"Baking {quantity} x {self.name} pizza... 🍕")


class Pasta(Product):
    def __init__(self, name: str, price: float, sauce_type: str = "Tomato"):
        super().__init__(name, price)
        self.sauce_type = sauce_type

    def get_info(self) -> str:
        return f"Pasta: {self.name} ({self.sauce_type} Sauce) - PHP {self.price:.2f}"

    def process_order(self, quantity: int):
        print(f"Boiling {quantity} x {self.name} pasta... 🍝")


if __name__ == "__main__":
    print("🌟 Welcome to Pasta & Pizza Hub - El Nido's Finest Italian Spot 🌟\n")

    pizza_names = [
        "Margherita", "Pepperoni", "Hawaiian", "Meat Lovers", "Veggie Supreme",
        "Four Cheese", "BBQ Chicken", "Seafood Delight", "Supreme", "Truffle Mushroom",
        "Spicy Italian", "Chicken Tikka", "Pesto Basil", "Tuna Mayo", "Bacon Ranch",
        "Mushroom Delight", "Olive and Feta", "Pineapple Special", "Classic Italian", "Garlic Shrimp"
    ]
    pizza_prices = [220, 280, 250, 320, 260, 290, 310, 340, 300, 350, 270, 295, 265, 240, 305, 255, 275, 285, 230, 315]

    pasta_names = [
        "Spaghetti Bolognese", "Fettuccine Alfredo", "Penne Arrabbiata", "Classic Lasagna",
        "Creamy Carbonara", "Pesto Genovese", "Shrimp Scampi", "Beef Stroganoff",
        "Cheese Ravioli", "Spinach Tortellini", "Potato Gnocchi", "Linguine Vongole",
        "Farfalle Primavera", "Rigatoni Amatriciana", "Fusilli Puttanesca",
        "Cannelloni Ricotta", "Lemon Orzo", "Tagliatelle Bolognese", "Bucatini Cacio e Pepe",
        "Macaroni Cheese"
    ]
    pasta_prices = [180, 210, 195, 250, 220, 200, 230, 240, 215, 205, 190, 225, 185, 235, 245, 260, 170, 255, 265, 175]

    products = {}
    for i in range(20):
        code = f"PIZ{i+1:02d}"
        products[code] = Pizza(pizza_names[i], pizza_prices[i])
    for i in range(20):
        code = f"PAS{i+1:02d}"
        products[code] = Pasta(pasta_names[i], pasta_prices[i])

    print("=== FULL MENU (20 Pizzas + 20 Pastas) ===")
    print("\nPIZZAS:")
    for code in [f"PIZ{i:02d}" for i in range(1, 21)]:
        print(f"  {code}: {products[code].get_info()}")
    print("\nPASTAS:")
    for code in [f"PAS{i:02d}" for i in range(1, 21)]:
        print(f"  {code}: {products[code].get_info()}")

    cart = []
    print("\n=== PLACE YOUR ORDER ===")
    print("Enter product code (e.g. PIZ05 or PAS12). Type 'done' to finish.")

    while True:
        choice = input("\nProduct code: ").strip().upper()
        if choice == "DONE":
            break
        if choice in products:
            try:
                qty = int(input(f"Quantity for {choice}: "))
                if qty > 0:
                    cart.append((products[choice], qty))
                    print(f"✅ Added {qty} × {products[choice].name}")
                else:
                    print("Quantity must be positive.")
            except ValueError:
                print("Invalid quantity.")
        else:
            print("❌ Invalid code. Please check the menu.")

    if not cart:
        print("\nNo items ordered. Thank you for visiting!")
    else:
        total = 0.0
        print("\n=== ORDER SUMMARY ===")
        for item, qty in cart:
            item_total = item.calculate_total(qty)
            total += item_total
            print(f"{qty} × {item.name:25} = PHP {item_total:,.2f}")
        print(f"{'-'*50}")
        print(f"GRAND TOTAL: PHP {total:,.2f}")

        name = input("\nEnter your full name: ").strip()
        phone = input("Enter your phone number: ").strip()

        while True:
            payment_method = input("Payment method (cash or credit): ").strip().lower()
            if payment_method in ["cash", "credit"]:
                break
            print("Please type 'cash' or 'credit'.")

        if payment_method == "credit":
            input("Enter card number (demo): ")
            print("✅ Card validated successfully.")

        confirm = input(f"\nConfirm payment of PHP {total:,.2f}? (y/n): ").strip().lower()
        if confirm == "y":
            order_code = random.randint(100000, 999999)
            print("\n🎉 PAYMENT CONFIRMED! Thank you, " + name + "!")
            print(f"📱 Your order code {order_code} has been sent via SMS to {phone}")
            print("\n🔧 Preparing your order now:")
            for item, qty in cart:
                item.process_order(qty)
            print(f"\n✅ Your order is being prepared. Please show code " + str(order_code) + " at the pickup counter.")
            print("We hope to see you again at Pasta & Pizza Hub!")
        else:
            print("\nOrder cancelled. Thank you for visiting Pasta & Pizza Hub!")
Was this helpful?
Share This Guide
ADVERTISEMENT
FAQ

Frequently Asked Questions

Pasta & Pizza Hub is a Python command-line program that simulates an Italian restaurant ordering system in El Nido, Palawan. It uses a base class Product and two subclasses Pizza and Pasta to demonstrate inheritance, polymorphism, and method overriding. Customers can choose from 20 pizzas and 20 pastas, place an order, and see polymorphic order preparation messages.
Both Pizza and Pasta override the process_order method from Product. When the program loops through the cart and calls item.process_order(qty), Python dynamically dispatches to the correct version – for Pizza objects, it prints a baking message 🍕; for Pasta objects, it prints a boiling message 🍝. No type checking is needed.
Each subclass adds a unique attribute (crust_type for Pizza, sauce_type for Pasta). By overriding get_info, they can include these details in the product description while still reusing the base name and price via super() calls in __init__. This keeps the output informative and avoids duplicating code.
super().__init__(name, price) calls the parent Product constructor to initialise the common attributes (name and price). This follows the DRY principle – the child classes only need to add their own specific attributes (crust_type or sauce_type) without repeating the base assignment.
Pizzas get codes PIZ01 to PIZ20, pastas get codes PAS01 to PAS20. They are stored in a dictionary with the code as key, enabling O(1) lookup when the user enters a code. This is a simple and efficient way to manage a menu.
After payment, a random 6-digit order code is generated using random.randint(100000, 999999). This simulates a real‑world order confirmation code that can be shown to the staff when picking up the food. It adds a touch of realism to the simulation.
COMMUNITY

Comments & Discussion

💬 Leave a Comment

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

C
Carlo Mendoza
March 2026
The dictionary lookup with formatted codes is so clean! I used to use lists and search by index, but this is much better. The polymorphism explanation with the baking/boiling messages really made it click for me.
L
Lea Santos
March 2026
I added a Salad class with a dressing attribute and it worked perfectly with the existing loop. Polymorphism is awesome! Great tutorial, thanks Glenn.
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 Inheritance and Polymorphism — Now Build Your Own Restaurant!

You have walked through every line of Pasta & Pizza Hub, from the base class to the polymorphic preparation loop. You now understand how method overriding, super(), and dictionary‑based product lookup work together to create a flexible, realistic ordering system.

Inheritance and polymorphism are fundamental tools in any Python developer's toolbox. They allow you to write code that is both reusable and easy to extend. The same principles you learned here appear in web frameworks, game engines, and data science libraries.

Your next step? Take the extension challenges seriously. Add a Salad class, implement discounts, or save orders to JSON. Each challenge will deepen your understanding and give you code you can proudly show off.

🔁 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…
📘 🐦 💼