Python

P
PythonHub
Progress 0%

Python

  • Home
  • History of Python
  • Applications of Python
  • Introduction To Python
    • What is Python
    • Character Set
    • Tokens in Python
    • Python Execution Mode
    • Variable And Identifiers
    • Data Types in Python
    • Operators And Expressions
    • Constants in Python
    • Assignment Statement
    • Input / Output in Python
    • Simple Python Scripts
    • Namespace in Python
    • šŸ“ Assignments
  • Operators in Python
    • Arithmetic Operators
    • Assignment Operators
    • Shorthand Assignment Operators
    • Relational Operators
    • Logical Operators
    • Bitwise Operators
    • Special Operators
    • šŸ“ Assignments
  • Input Output in Python
    • Accept Input
    • Output Formatting
    • šŸ“ Assignments
  • Conditional Statement
    • Decision Making
    • if Statement
    • IF-ELSE STATEMENT
    • IF-ELSE LADDER
    • NESTED IF-ELSE
    • Short Hand IF-ELSE
    • šŸ“ Assignments
  • Loops
    • Introduction to Loops
    • While Loop
    • Nested While Loop
    • šŸ“ While Loop Assignments
    • For Loop
    • For Loop Examples
    • Nested For Loop
    • Nested For Loop Examples
    • Infinite While Loops
    • Infinite For Loops
    • Break, Continue and Else in Loops
    • Difference Between For and While Loop
    • For Each Loop
    • šŸ“ For Each Assignments
    • šŸ“ All Loops Assignments
  • List
    • List in Python
    • Access List Elements
    • List Functions
    • Iterate (Loop) List
    • List Comprehension
    • šŸ“ Assignments
  • Tuple
    • Tuple in Python
    • Access Tuple Elements
    • Tuple Functions
    • Iterate (Loop) Tuple
    • Unpack Tuple
    • Tuple Comprehension
    • šŸ“ Assignments
  • Set
    • Set in Python
    • Access Set Elements
    • Set Methods
    • Iterate (Loop) Set
    • Pack/Unpack Set
    • Set Comprehension
    • šŸ“ Assignments
  • Dictionary
    • Dictionary
    • Access Dictionary Items
    • Dictionary Methods
    • Iterate (Loop) Dictionary
    • Formatting Dictionaries
    • Nested Dictionaries
    • Dictionary Comprehension
    • šŸ“ Assignments
  • Diff List Tuple Set Dictionary
    • List vs Tuple
    • List vs Set
    • List vs Dictionary
    • Tuple vs Set
    • Tuple vs Dictionary
    • Dictionary vs Set
    • šŸ“ Assignments
  • Exception
    • Error vs Exception
    • Types of Exception
    • Exception Handling
    • User Defined Exception
    • Logging Exception
    • šŸ“ Assignments
  • Functions
    • Introduction to Functions
    • Modular Programming
    • Types of Functions
    • Inbuilt Functions
    • Need For User-Defined Function
    • Elements of User Defined Function
    • Function Arguments
    • Nesting of Functions
    • Recursion
    • Global Local and Non Local
    • Python Lambda Functions
    • šŸ“ Assignments
  • Python Module
    • Introduction to Module
    • Inbuilt Modules in Python
    • User Defined Module
    • šŸ“ Assignments
  • File Handling
    • Introduction to Files
    • Create File
    • Read Files
    • Write to File
    • Rename File
    • Copy File
    • Move File
    • List Files in Directory
    • Binary Files
    • Zipping and Unzipping Files
    • šŸ“ Assignments
  • Strings
    • Basics of Strings
    • String Special Operators
    • String Formatting Operators
    • String Methods
    • šŸ“ Assignments
  • Regular Expressions
  • Python OOPS
    • Basics of Object Oriented
    • What are Classes and Objects?
    • Creating Class and Object
    • OOP vs Procedural Programming
    • Difference Between Classes and Objects
    • Constructors
    • Destructor
    • Built Class Methods and Attributes
    • Class and Instance Variables
    • Inheritance in Python
    • Single Inheritance
    • Multiple Inheritance
    • Multilevel Inheritance
    • Hierarchical Inheritance
    • Hybrid Inheritance
    • Abstraction
    • Method Overriding
    • Abstract Method
    • Interfaces in Python
    • Abstract Class vs Interface
    • Public, Private and Protected
    • Overloading vs Overriding
    • Inheritance vs Composition
    • Encapsulation
    • Polymorphism
    • Inner Classes
    • šŸ“ Assignments
  • Advanced Python
    • Iterator in Python
    • Generator in Python
    • Decorator in Python
    • Type Hints in Python
    • Async/Await Programming
    • Dataclasses in Python
    • Context Managers in Python
    • Property Decorator in Python
    • __slots__ in Python
    • Enums in Python
    • Walrus Operator in Python
    • Match-Case in Python
    • šŸ“ Assignments
  • Python Standard Library
    • Collections Module
    • Itertools Module
    • Functools Module
    • Datetime Module
    • JSON Module
    • OS Module
    • Sys Module
    • Random Module
    • Math Module
    • šŸ“ Assignments
  • Python Testing
    • Unit Testing in Python
    • Pytest Framework
    • Mocking in Python
    • šŸ“ Assignments
  • Python Best Practices
    • PEP 8 Style Guide
    • Docstrings in Python
    • Logging in Python
    • Code Optimization Tips
    • Debugging Techniques
    • šŸ“ Assignments
  • MySQL Database in Python
    • Introduction to MySQL with Python
    • DBMS vs File System
    • Connecting to MySQL Database
    • Create Database in MySQL
    • Create Table in MySQL
    • Insert Data in MySQL
    • Insert Multiple Rows
    • Select Data from MySQL
    • WHERE Clause in MySQL
    • Update Data in MySQL
    • Delete Data from MySQL
    • Parameterized Queries
    • Transaction Management
    • Error Handling
    • Connection Pooling
    • MySQL Drivers Guide
    • Joins in MySQL
    • Aggregation Functions
    • Backup MySQL Database
    • Best Practices
    • šŸ“ Assignments
  • MySQL Database Operations
    • SELECT Statement
    • MySQL Operators
    • DDL Statements
    • DML Statements
    • Subqueries
    • JOIN Operations
    • Aggregation
    • Case Study
    • šŸ“ Assignments
  • Graphics in Python
  • Threads in Python
    • Introduction to Threads
    • Process vs Threads
    • Concurrent Programming & GIL
    • Uses of Threads
    • Creating Threads
    • Single Tasking
    • Multi Tasking
    • Thread Synchronization
    • šŸ“ Assignments
  • Interview Questions & Answers
  • Python Case Studies
  • Multiple Choice Questions
  • šŸ“ Practice Problems
Get in Touch
  • tech2dsm@gmail.com

© Sankalan Data Tech

Python Language Interactive Tutorial

Python: Method Overriding

Python Method Overriding - Complete Guide

Learn how to redefine parent class methods in child classes.

Created by Sankalan Data Tech Team Verified
Data Engineers, Analysts, Scientists & Trainers
Created by experienced Python developers, data engineers, and data scientists to make programming easy through practical examples, real-world experience, and clear explanations.
On this page:
  • What is Method Overriding?
  • Why Do We Need Method Overriding?
  • How Method Overriding Works
  • Using super() to Call Parent Methods
  • Overriding Constructors
  • Real-World Examples
  • Best Practices
  • Try It Yourself
  • Quick Quiz
  • Frequently Asked Questions
Share this tutorial:
Twitter LinkedIn Facebook WhatsApp Reddit Telegram Email Copy Link
What You'll Learn Here
  • What is method overriding — redefining parent class methods in child classes
  • Why we need it — customizing behavior for specific child classes
  • How it works — the mechanics of overriding
  • super() method — calling parent methods from child classes
  • Constructor overriding — working with __init__ in child classes
  • Real-world use — practical examples you can use

What is Method Overriding?

Method overriding is a feature in Python that allows a child class to redefine a method that it inherited from its parent class. When you override a method, you're telling Python: "I want this method to work differently for this specific child class."

Think of it like a family recipe. Your grandmother's recipe (parent class) is great, but you want to add your own twist to it. You still use the same basic ingredients and steps, but you change a few things to make it your own. That's exactly what method overriding is — taking what you inherited and making it better suited for your needs.

Method overriding is a key part of polymorphism — the ability to use the same method name across different classes, but each class implements it differently.

šŸ’” Key concept: Method overriding allows child classes to provide their own implementation of a method that's already defined in the parent class. It's like customizing something you inherited to fit your specific needs.

Why Do We Need Method Overriding?

1

Customizing Behavior for Each Child

Without method overriding, all child classes would have the exact same behavior. But in real life, different types need different behavior. A dog and a cat both make sounds, but they make different sounds. Method overriding lets you give each child its own unique behavior while still being part of the same family.

# Method overriding in action

class Animal:
    """Parent class - all animals share this"""
    
    def __init__(self, name):
        self.name = name
    
    def make_sound(self):
        return f"{self.name} makes a generic sound"
    
    def move(self):
        return f"{self.name} moves somehow"
    
    def eat(self):
        return f"{self.name} eats food"

# Without overriding — all animals behave the same
class GenericAnimal(Animal):
    pass

generic = GenericAnimal("Something")
print(generic.make_sound())  # Something makes a generic sound

# WITH overriding — each animal has its own behavior
class Dog(Animal):
    def make_sound(self):
        return f"{self.name} says Woof!"
    
    def move(self):
        return f"{self.name} runs on four legs"
    
    def eat(self):
        return f"{self.name} eats dog food"

class Cat(Animal):
    def make_sound(self):
        return f"{self.name} says Meow!"
    
    def move(self):
        return f"{self.name} walks silently"
    
    def eat(self):
        return f"{self.name} eats cat food"

class Bird(Animal):
    def make_sound(self):
        return f"{self.name} says Chirp!"
    
    def move(self):
        return f"{self.name} flies in the sky"
    
    def eat(self):
        return f"{self.name} eats seeds"

# Using overridden methods
print("=== DOG ===")
dog = Dog("Buddy")
print(dog.make_sound())  # Buddy says Woof!
print(dog.move())        # Buddy runs on four legs
print(dog.eat())         # Buddy eats dog food

print("\n=== CAT ===")
cat = Cat("Whiskers")
print(cat.make_sound())  # Whiskers says Meow!
print(cat.move())        # Whiskers walks silently
print(cat.eat())         # Whiskers eats cat food

print("\n=== BIRD ===")
bird = Bird("Tweety")
print(bird.make_sound()) # Tweety says Chirp!
print(bird.move())       # Tweety flies in the sky
print(bird.eat())        # Tweety eats seeds

print("\nāœ… Method overriding gives each animal its own unique behavior!")
print("All animals have the same method names, but different implementations.")

Why override methods:

  • Customization — each child can behave differently
  • Flexibility — you can change behavior without changing parent class
  • Polymorphism — same method name, different implementations
  • Real-world modeling — different types have different behaviors
  • Code reuse — still inherits common methods, overrides only what's different

Quick Check: Why would you override a method in a child class? (Answer: To customize behavior for that specific child class)

How Method Overriding Works

2

Understanding the Mechanics

When you call a method on an object, Python looks for it in a specific order. It first checks the child class. If the method exists there, Python uses it — that's method overriding in action. If it doesn't find it there, it goes up to the parent class.

This is called the method lookup chain. It's how Python decides which version of a method to use. The child's version always takes priority if it exists.

# How method overriding works

class Parent:
    def greet(self):
        return "Hello from Parent"
    
    def farewell(self):
        return "Goodbye from Parent"
    
    def common(self):
        return "This is common to everyone"

class Child(Parent):
    # Overriding the greet method
    def greet(self):
        return "Hello from Child (overridden!)"
    
    # Not overriding farewell — uses parent's version
    # Not overriding common — uses parent's version

class Grandchild(Child):
    # Overriding the greet method again
    def greet(self):
        return "Hello from Grandchild (overridden again!)"
    
    # Overriding farewell too
    def farewell(self):
        return "Goodbye from Grandchild"

# Testing the lookup chain
print("=== PARENT OBJECT ===")
parent = Parent()
print(f"greet(): {parent.greet()}")
print(f"farewell(): {parent.farewell()}")
print(f"common(): {parent.common()}")

print("\n=== CHILD OBJECT ===")
child = Child()
print(f"greet(): {child.greet()}")        # Child's version
print(f"farewell(): {child.farewell()}")  # Parent's version (not overridden)
print(f"common(): {child.common()}")      # Parent's version (not overridden)

print("\n=== GRANDCHILD OBJECT ===")
grandchild = Grandchild()
print(f"greet(): {grandchild.greet()}")        # Grandchild's version
print(f"farewell(): {grandchild.farewell()}")  # Grandchild's version (overridden)
print(f"common(): {grandchild.common()}")      # Parent's version (not overridden)

print("\n=== LOOKUP CHAIN ===")
print("When grandchild.greet() is called:")
print("1. Python checks Grandchild class → found it!")
print("2. Uses Grandchild's version")
print("\nWhen grandchild.farewell() is called:")
print("1. Python checks Grandchild class → not found")
print("2. Checks Child class → not found")
print("3. Checks Parent class → found it!")
print("4. Uses Parent's version")

print("\nThe first matching method found in the chain is used.")

How method overriding works:

  • Child first — Python looks in the child class first
  • Parent next — if not found in child, looks in parent
  • Continues up — keeps going up the inheritance chain
  • First match wins — the first method found is used
  • Overriding means replacing — the child's version replaces the parent's

Quick Check: Where does Python first look for a method when called on an object? (Answer: In the object's class first, then up the inheritance chain)

Using super() to Call Parent Methods

3

Accessing the Parent's Version

Sometimes you want to extend the parent's behavior rather than completely replace it. That's where super() comes in. super() allows you to call the parent class's version of a method from inside the child class.

This is like saying: "Do what the parent does, but also do this extra stuff." It's very useful when you want to add to the parent's behavior rather than change it completely.

# Using super() to call parent methods

class Vehicle:
    def __init__(self, brand, model):
        self.brand = brand
        self.model = model
        self.is_running = False
    
    def start(self):
        self.is_running = True
        return f"Engine started for {self.brand} {self.model}"
    
    def stop(self):
        self.is_running = False
        return f"Engine stopped for {self.brand} {self.model}"
    
    def get_info(self):
        return f"{self.brand} {self.model}"

# Child class that uses super() to extend parent behavior
class ElectricCar(Vehicle):
    def __init__(self, brand, model, battery_capacity):
        # Call parent __init__ first
        super().__init__(brand, model)
        self.battery_capacity = battery_capacity
        self.charge_level = 100
    
    def start(self):
        # Call parent's start method first
        parent_result = super().start()
        # Then add electric car specific behavior
        return f"{parent_result} with {self.charge_level}% battery"
    
    def stop(self):
        parent_result = super().stop()
        return f"{parent_result} (electric motors disengaged)"
    
    def get_info(self):
        # Get parent's info and add battery info
        parent_info = super().get_info()
        return f"{parent_info} with {self.battery_capacity}kWh battery"

# Child class that uses super() with additional parameters
class SportsCar(Vehicle):
    def __init__(self, brand, model, horse_power, top_speed):
        super().__init__(brand, model)
        self.horse_power = horse_power
        self.top_speed = top_speed
        self.sport_mode = False
    
    def start(self):
        parent_result = super().start()
        self.sport_mode = True
        return f"{parent_result} in sport mode! šŸŽļø"
    
    def get_info(self):
        return f"{super().get_info()} ({self.horse_power}HP, {self.top_speed}mph)"

# Using the classes
print("=== ELECTRIC CAR ===")
tesla = ElectricCar("Tesla", "Model 3", 75)
print(tesla.start())    # Engine started... with 100% battery
print(tesla.get_info()) # Tesla Model 3 with 75kWh battery
print(tesla.stop())     # Engine stopped (electric motors disengaged)

print("\n=== SPORTS CAR ===")
ferrari = SportsCar("Ferrari", "F8", 710, 211)
print(ferrari.start())  # Engine started... in sport mode!
print(ferrari.get_info()) # Ferrari F8 (710HP, 211mph)

print("\nāœ… super() lets you call the parent's version while adding your own!")
print("This is called 'extending' behavior rather than replacing it.")

super() key points:

  • Extends behavior — adds to parent's behavior instead of replacing it
  • Maintains parent logic — ensures parent's code still runs
  • Constructor chaining — essential for proper initialization
  • Follows MRO — works correctly with multiple inheritance
  • Code reuse — don't duplicate parent's code

Quick Check: When should you use super() in an overridden method? (Answer: When you want to extend the parent's behavior rather than completely replace it)

Overriding Constructors

4

Working with __init__ in Child Classes

The __init__ method (constructor) is just like any other method — you can override it in child classes. When you do, it's very important to call the parent's __init__ using super().__init__(). Otherwise, the parent's attributes won't be initialized.

Think of it like building a house. The parent class lays the foundation and builds the walls. The child class adds the roof and finishes the interior. You need the foundation and walls first — that's why you call the parent's constructor first.

# Overriding constructors

class Product:
    """Parent class - basic product"""
    
    def __init__(self, product_id, name, price):
        print(f"Product.__init__ called for {name}")
        self.product_id = product_id
        self.name = name
        self.price = price
        self.in_stock = True
    
    def get_info(self):
        return f"ID: {self.product_id}, Name: {self.name}, Price: ${self.price}"

# Good child class — calls super().__init__()
class ElectronicProduct(Product):
    def __init__(self, product_id, name, price, brand, warranty_years):
        # MUST call parent's __init__ first
        super().__init__(product_id, name, price)
        print(f"ElectronicProduct.__init__ called for {name}")
        self.brand = brand
        self.warranty_years = warranty_years
    
    def get_info(self):
        return f"{super().get_info()}, Brand: {self.brand}, Warranty: {self.warranty_years} years"

# Bad child class — forgets to call super().__init__()
class BrokenProduct(Product):
    def __init__(self, product_id, name, price, discount):
        # MISSING: super().__init__(product_id, name, price)
        print(f"BrokenProduct.__init__ called for {name}")
        self.discount = discount

# Good child class with extra logic
class BookProduct(Product):
    def __init__(self, product_id, name, price, author, pages):
        # Can do some validation before calling parent
        if pages <= 0:
            raise ValueError("Pages must be greater than 0")
        
        # Then call parent
        super().__init__(product_id, name, price)
        print(f"BookProduct.__init__ called for {name}")
        self.author = author
        self.pages = pages
    
    def get_info(self):
        return f"{super().get_info()}, Author: {self.author}, Pages: {self.pages}"

# Using good classes
print("=== ELECTRONIC PRODUCT ===")
laptop = ElectronicProduct("E001", "Laptop", 999.99, "Dell", 2)
print(laptop.get_info())

print("\n=== BOOK PRODUCT ===")
book = BookProduct("B001", "Python Guide", 49.99, "John Smith", 350)
print(book.get_info())

# The broken class would cause an error:
# broken = BrokenProduct("B001", "Broken", 100, 10)
# print(broken.name)  # AttributeError! name was never set

print("\nāœ… Always call super().__init__() in child classes!")
print("This ensures all parent attributes are properly initialized.")

Constructor overriding key points:

  • Call super().__init__() — always call the parent constructor
  • Initialize parent first — parent attributes must be set before child attributes
  • Can add validation — you can validate before calling parent
  • Can add extra logic — do additional setup after parent init
  • Never forget super() — forgetting causes AttributeError

Quick Check: What happens if you don't call super().__init__() in a child class? (Answer: Parent attributes won't be initialized, causing AttributeError)

Real-World Examples

5

Seeing Method Overriding in Action

# Real-world example: A Notification System

class Notification:
    """Parent class - basic notification"""
    
    def __init__(self, recipient, message):
        self.recipient = recipient
        self.message = message
        self.sent_at = None
    
    def send(self):
        """Send the notification - generic version"""
        self.sent_at = "now"
        return f"Sending notification to {self.recipient}: {self.message}"
    
    def get_status(self):
        status = "Sent" if self.sent_at else "Pending"
        return f"Status: {status} at {self.sent_at or 'not sent yet'}"
    
    def format_message(self):
        """Format the message - can be overridden"""
        return self.message

# Child class 1 - Email Notification
class EmailNotification(Notification):
    def __init__(self, recipient, message, subject, cc=None):
        # Override constructor - add email-specific attributes
        super().__init__(recipient, message)
        self.subject = subject
        self.cc = cc or []
        self.is_html = False
    
    def send(self):
        # Override send - add email-specific behavior
        super().send()  # Call parent's send to set sent_at
        cc_text = f", CC: {self.cc}" if self.cc else ""
        return f"šŸ“§ Sending email to {self.recipient}{cc_text}: {self.subject}"
    
    def format_message(self):
        # Override format - add HTML support
        if self.is_html:
            return f"

{self.subject}

{self.message}

" return f"SUBJECT: {self.subject}\n\n{self.message}" def add_attachment(self, file_name): """Email-specific method""" return f"Attached {file_name} to email" # Child class 2 - SMS Notification class SMSNotification(Notification): def __init__(self, recipient, message, sender_id): super().__init__(recipient, message) self.sender_id = sender_id self.message_limit = 160 self.is_sent = False def send(self): # Override send - add SMS-specific behavior if len(self.message) > self.message_limit: return f"āŒ SMS too long! {len(self.message)} chars (max {self.message_limit})" self.sent_at = "now" self.is_sent = True return f"šŸ“± Sending SMS from {self.sender_id} to {self.recipient}" def format_message(self): # Override format - truncate for SMS if len(self.message) > self.message_limit: return self.message[:self.message_limit] + "..." return self.message def get_delivery_report(self): """SMS-specific method""" return f"Delivery report: {'Delivered' if self.is_sent else 'Pending'}" # Child class 3 - Push Notification class PushNotification(Notification): def __init__(self, recipient, message, app_name, priority="normal"): super().__init__(recipient, message) self.app_name = app_name self.priority = priority self.actions = [] def send(self): # Override send - add push notification behavior super().send() # Call parent's send actions_text = f" with actions: {self.actions}" if self.actions else "" return f"šŸ“² Sending push notification from {self.app_name} to {self.recipient}{actions_text} (Priority: {self.priority})" def format_message(self): # Override format - add priority indicator priority_emoji = "šŸ”“" if self.priority == "high" else "🟢" return f"{priority_emoji} {self.message}" def add_action(self, action_name): """Push notification-specific method""" self.actions.append(action_name) return f"Added action: {action_name}" # Using the notification system print("=" * 50) print("NOTIFICATION SYSTEM WITH METHOD OVERRIDING") print("=" * 50) def send_notification(notification): """Function that works with any notification type""" print(notification.send()) print(f"Formatted: {notification.format_message()}") print(notification.get_status()) print("\n=== EMAIL NOTIFICATION ===") email = EmailNotification( "user@example.com", "Hello! This is a test message.", "Test Email", cc=["boss@example.com"] ) email.is_html = True send_notification(email) print(email.add_attachment("report.pdf")) print("\n=== SMS NOTIFICATION ===") sms = SMSNotification("+1234567890", "Your code is 123456", "MyApp") send_notification(sms) print(sms.get_delivery_report()) print("\n=== PUSH NOTIFICATION ===") push = PushNotification("user123", "New message from Alice!", "MyApp", "high") push.add_action("Reply") push.add_action("Delete") send_notification(push) print("\nāœ… Each notification type has its own behavior!") print("The send_notification function works with ALL notification types.")

Real-world example key points:

  • Parent class — Notification defines common interface (send, format_message, get_status)
  • EmailNotification — adds subject, CC, HTML support, attachments
  • SMSNotification — adds character limit, sender_id, delivery reports
  • PushNotification — adds app_name, priority, actions
  • Polymorphism — send_notification() works with any notification type

Quick Check: What does the send_notification function demonstrate? (Answer: Polymorphism — it works with any class that inherits from Notification)

Best Practices for Method Overriding

6

Using Method Overriding Effectively

# Best practices for method overriding

# 1. Always call super().__init__() in child class constructors
class Base:
    def __init__(self, value):
        self.value = value

class Child(Base):
    def __init__(self, value, extra):
        super().__init__(value)  # āœ… Good
        self.extra = extra

# 2. Keep the method signature consistent
#  Good — same parameters
class Parent:
    def process(self, data):
        return f"Processing {data}"

class Child(Parent):
    def process(self, data):
        return f"Child processing {data}"

#  Bad — different parameters (can cause confusion)
class BadChild(Parent):
    def process(self, data, extra):  # āš ļø Different signature
        return f"Processing {data} with {extra}"

# 3. Use super() to extend, not just replace
class Worker:
    def work(self):
        return "Working hard"

class Manager(Worker):
    def work(self):
        return f"{super().work()} and managing team"  # āœ… Extends

#  Bad — completely replaces without using super
class BadManager(Worker):
    def work(self):
        return "Managing team"  # āŒ Loses "Working hard"

# 4. Document why you're overriding
class Animal:
    def speak(self):
        """Return the animal's sound"""
        return "Generic sound"

class Dog(Animal):
    def speak(self):
        """Return the dog's sound — overridden to be specific"""
        return "Woof!"

# 5. Don't override methods that don't need it
#  Only override when you need different behavior

# 6. Test overridden methods
class Tester:
    def test(self):
        return "Testing..."

class TestSub(Tester):
    def test(self):
        result = super().test()
        return f"Extended: {result}"

# 7. Be careful with private methods
class Private:
    def __method(self):
        return "Private method"  # Name mangling makes this hard to override

class BadPrivate(Private):
    def __method(self):
        return "Trying to override"  # This won't work properly

Best practices summary:

  • Call super().__init__() — always in child class constructors
  • Keep signatures consistent — same parameters as parent
  • Extend, don't replace — use super() to add to parent behavior
  • Document your overrides — explain why you're overriding
  • Only override when needed — don't override unnecessarily
  • Test your overrides — make sure they work correctly
  • Avoid private method overrides — name mangling makes it tricky

Quick Check: Should you always keep the same method signature when overriding? (Answer: Yes, to maintain consistency and avoid confusion)

Try It Yourself

Experiment with method overriding in the editor below.

Loading Pyodide... 0%
Python Code Editor
========================================
METHOD OVERRIDING PRACTICE
========================================

1. CREATING A PARENT CLASS

2. CREATING CHILD CLASSES

3. USING THE CLASSES
MICROWAVE:
Samsung microwave is heating at 1000W
šŸ• Heating food in Samsung microwave
Samsung MG-101: ON

COFFEE MAKER:
Breville coffee maker is brewing 4 cups
ā˜• Brewed coffee (total: 1)
ā˜• Brewed coffee (total: 2)
Breville Barista: ON

BLENDER:
Vitamix blender is blending at speed 6
🄤 Blending smoothie in Vitamix blender
Vitamix Pro-750: ON

4. POLYMORPHISM IN ACTION
Samsung: šŸ• Heating food in Samsung microwave
Breville: ā˜• Brewed coffee (total: 3)
Vitamix: 🄤 Blending smoothie in Vitamix blender

Method overriding practice complete!
šŸ†

You've Got It!

You now understand method overriding in Python. You know how to redefine parent methods in child classes, use super() to extend behavior, and override constructors properly.

Quick Quiz

Test what you've learned:

1. What is method overriding?
2. Where does Python first look for a method when it's called on an object?
3. What does super() do in an overridden method?
4. Why should you call super().__init__() in a child class?
5. What is the main benefit of method overriding?

Frequently Asked Questions

What's the difference between method overriding and method overloading? ā–¼

Method overriding is when a child class redefines a method from its parent class (same name, same parameters). Method overloading is when you have multiple methods with the same name but different parameters. Python doesn't support method overloading traditionally — you can achieve similar results with default parameters or *args.

Do I have to call super() when overriding a method? ā–¼

No, it's not required. You only call super() when you want to extend the parent's behavior. If you want to completely replace the parent's behavior, you don't need to call super(). But for constructors (__init__), it's strongly recommended to call super().__init__() to ensure all parent attributes are initialized.

Can I override a method in a grandchild class? ā–¼

Yes! You can override methods at any level of the inheritance chain. A grandchild class can override a method that was defined in the parent or grandparent. The lookup chain always starts at the object's class and goes up, so the most specific (lowest in the chain) version wins.

What happens if I change the method signature when overriding? ā–¼

Technically, you can change the signature (different parameters), but it's not recommended. It can cause confusion and break polymorphic behavior. If you need different parameters, it's better to use default parameters or *args/*kwargs to keep the interface consistent.

What's a common interview question about method overriding? ā–¼

Common questions include: "What is method overriding and why is it used?" "Explain the difference between overriding and overloading." "What is the purpose of super()?" "How does Python determine which method to call when there's an override?" and "What happens if you forget to call super().__init__()?"

When should I use method overriding vs composition? ā–¼

Use method overriding when you have an "is-a" relationship and you want to customize behavior while keeping the same interface. Use composition when you have a "has-a" relationship and you want to combine behaviors from multiple sources. Method overriding is for inheritance hierarchies; composition is for building complex objects from simpler ones.

Where to Go From Here

Now that you understand method overriding, check out these related topics:

Overloading vs Overriding

Learn the key differences between these two concepts.

Learn More →

Abstract Methods

Learn about methods that must be implemented by child classes.

Learn More →

Polymorphism

Learn how overriding enables polymorphic behavior.

Learn More →
Interview Resources
  • Python Syntax & Variables Interview Questions
  • Top SQL Interview Questions & Answers
  • SQL Joins: Displaying Data from Multiple Tables FAQ
  • Python Lists and Dictionaries Interview Questions
  • Python OOP Interview Questions
  • SQL Set Operators Interview Questions
Previous: Abstraction Next: Abstract Method →