Inheritance
Inheritance is a mechanism where a new class (derived class or subclass) inherits attributes and methods from an existing class (base class or superclass). This allows you to create a hierarchy of classes that share common functionality while allowing specialization.
Understanding Inheritance
Section titled “Understanding Inheritance”Inheritance creates an “is-a” relationship. For example:
- A
Caris-aVehicle - A
Dogis-anAnimal - A
Manageris-anEmployee
Basic Inheritance
Section titled “Basic Inheritance”class Vehicle: """Base class (parent/superclass)""" def __init__(self, brand: str, model: str, year: int): self.brand = brand self.model = model self.year = year
def start(self): return f"{self.brand} {self.model} started."
def stop(self): return f"{self.brand} {self.model} stopped."
def get_info(self): return f"{self.brand} {self.model} ({self.year})"
class Car(Vehicle): """Derived class (child/subclass) - inherits from Vehicle""" def __init__(self, brand: str, model: str, year: int, num_doors: int): super().__init__(brand, model, year) # Call parent constructor self.num_doors = num_doors # Additional attribute
def honk(self): """New method specific to Car""" return "Beep beep!"
class Motorcycle(Vehicle): """Another derived class""" def __init__(self, brand: str, model: str, year: int, bike_type: str): super().__init__(brand, model, year) self.bike_type = bike_type # Additional attribute
def wheelie(self): """New method specific to Motorcycle""" return "Doing a wheelie!"
# Usagecar = Car("Toyota", "Camry", 2020, 4)print(car.start()) # Inherited method: "Toyota Camry started."print(car.honk()) # Car-specific method: "Beep beep!"print(car.get_info()) # Inherited method: "Toyota Camry (2020)"
motorcycle = Motorcycle("Yamaha", "YZF-R3", 2021, "Sport")print(motorcycle.start()) # Inherited method: "Yamaha YZF-R3 started."print(motorcycle.wheelie()) # Motorcycle-specific method: "Doing a wheelie!"// Base class (parent/superclass)public class Vehicle { protected String brand; protected String model; protected int year;
public Vehicle(String brand, String model, int year) { this.brand = brand; this.model = model; this.year = year; }
public String start() { return brand + " " + model + " started."; }
public String stop() { return brand + " " + model + " stopped."; }
public String getInfo() { return brand + " " + model + " (" + year + ")"; }}
// Derived class (child/subclass) - inherits from Vehiclepublic class Car extends Vehicle { private int numDoors;
public Car(String brand, String model, int year, int numDoors) { super(brand, model, year); // Call parent constructor this.numDoors = numDoors; // Additional attribute }
// New method specific to Car public String honk() { return "Beep beep!"; }}
// Another derived classpublic class Motorcycle extends Vehicle { private String bikeType;
public Motorcycle(String brand, String model, int year, String bikeType) { super(brand, model, year); this.bikeType = bikeType; // Additional attribute }
// New method specific to Motorcycle public String wheelie() { return "Doing a wheelie!"; }}
// Usagepublic class Main { public static void main(String[] args) { Car car = new Car("Toyota", "Camry", 2020, 4); System.out.println(car.start()); // Inherited method: "Toyota Camry started." System.out.println(car.honk()); // Car-specific method: "Beep beep!" System.out.println(car.getInfo()); // Inherited method: "Toyota Camry (2020)"
Motorcycle motorcycle = new Motorcycle("Yamaha", "YZF-R3", 2021, "Sport"); System.out.println(motorcycle.start()); // Inherited method: "Yamaha YZF-R3 started." System.out.println(motorcycle.wheelie()); // Motorcycle-specific method: "Doing a wheelie!" }}classDiagram
class Vehicle {
+brand: str
+model: str
+year: int
+start() str
+stop() str
+get_info() str
}
class Car {
+num_doors: int
+honk() str
}
class Motorcycle {
+bike_type: str
+wheelie() str
}
Vehicle <|-- Car
Vehicle <|-- Motorcycle
Method Overriding
Section titled “Method Overriding”Subclasses can override parent methods to provide specialized behavior:
class Vehicle: def __init__(self, brand: str, model: str, year: int): self.brand = brand self.model = model self.year = year
def start(self): return f"{self.brand} {self.model} started."
def fuel_type(self): return "Unknown fuel type"
class Car(Vehicle): def start(self): """Override parent method with specialized behavior""" return f"{self.brand} {self.model} car started with a roar!"
def fuel_type(self): """Override to specify fuel type""" return "Gasoline or Electric"
class ElectricCar(Car): def start(self): """Further override for electric cars""" return f"{self.brand} {self.model} silently started (electric motor)"
def fuel_type(self): """Override to specify electric""" return "Electric"
car = Car("Toyota", "Camry", 2020)print(car.start()) # "Toyota Camry car started with a roar!"print(car.fuel_type()) # "Gasoline or Electric"
electric_car = ElectricCar("Tesla", "Model 3", 2023)print(electric_car.start()) # "Tesla Model 3 silently started (electric motor)"print(electric_car.fuel_type()) # "Electric"public class Vehicle { protected String brand; protected String model; protected int year;
public Vehicle(String brand, String model, int year) { this.brand = brand; this.model = model; this.year = year; }
public String start() { return brand + " " + model + " started."; }
public String fuelType() { return "Unknown fuel type"; }}
public class Car extends Vehicle { public Car(String brand, String model, int year) { super(brand, model, year); }
@Override public String start() { // Override parent method with specialized behavior return brand + " " + model + " car started with a roar!"; }
@Override public String fuelType() { // Override to specify fuel type return "Gasoline or Electric"; }}
public class ElectricCar extends Car { public ElectricCar(String brand, String model, int year) { super(brand, model, year); }
@Override public String start() { // Further override for electric cars return brand + " " + model + " silently started (electric motor)"; }
@Override public String fuelType() { // Override to specify electric return "Electric"; }}
// Usagepublic class Main { public static void main(String[] args) { Car car = new Car("Toyota", "Camry", 2020); System.out.println(car.start()); // "Toyota Camry car started with a roar!" System.out.println(car.fuelType()); // "Gasoline or Electric"
ElectricCar electricCar = new ElectricCar("Tesla", "Model 3", 2023); System.out.println(electricCar.start()); // "Tesla Model 3 silently started (electric motor)" System.out.println(electricCar.fuelType()); // "Electric" }}Using super()
Section titled “Using super()”The super() function/keyword allows you to call methods from the parent class:
class Employee: def __init__(self, name: str, employee_id: int): self.name = name self.employee_id = employee_id
def get_info(self): return f"Employee: {self.name} (ID: {self.employee_id})"
class Manager(Employee): def __init__(self, name: str, employee_id: int, department: str): super().__init__(name, employee_id) # Call parent __init__ self.department = department
def get_info(self): """Extend parent method using super()""" base_info = super().get_info() # Call parent method return f"{base_info}, Department: {self.department}"
class Director(Manager): def __init__(self, name: str, employee_id: int, department: str, budget: float): super().__init__(name, employee_id, department) # Call Manager's __init__ self.budget = budget
def get_info(self): """Further extend using super()""" manager_info = super().get_info() # Call Manager's get_info return f"{manager_info}, Budget: ${self.budget:,.2f}"
manager = Manager("Alice", 101, "Engineering")print(manager.get_info())# "Employee: Alice (ID: 101), Department: Engineering"
director = Director("Bob", 102, "Engineering", 1000000.0)print(director.get_info())# "Employee: Bob (ID: 102), Department: Engineering, Budget: $1,000,000.00"public class Employee { protected String name; protected int employeeId;
public Employee(String name, int employeeId) { this.name = name; this.employeeId = employeeId; }
public String getInfo() { return "Employee: " + name + " (ID: " + employeeId + ")"; }}
public class Manager extends Employee { private String department;
public Manager(String name, int employeeId, String department) { super(name, employeeId); // Call parent constructor this.department = department; }
@Override public String getInfo() { // Extend parent method using super String baseInfo = super.getInfo(); // Call parent method return baseInfo + ", Department: " + department; }}
public class Director extends Manager { private double budget;
public Director(String name, int employeeId, String department, double budget) { super(name, employeeId, department); // Call Manager's constructor this.budget = budget; }
@Override public String getInfo() { // Further extend using super String managerInfo = super.getInfo(); // Call Manager's getInfo return managerInfo + ", Budget: $" + String.format("%,.2f", budget); }}
// Usagepublic class Main { public static void main(String[] args) { Manager manager = new Manager("Alice", 101, "Engineering"); System.out.println(manager.getInfo()); // "Employee: Alice (ID: 101), Department: Engineering"
Director director = new Director("Bob", 102, "Engineering", 1000000.0); System.out.println(director.getInfo()); // "Employee: Bob (ID: 102), Department: Engineering, Budget: $1,000,000.00" }}Real-World Example: E-commerce System
Section titled “Real-World Example: E-commerce System”class Product: """Base class for all products""" def __init__(self, name: str, price: float, sku: str): self.name = name self.price = price self.sku = sku
def calculate_shipping(self, weight: float) -> float: """Base shipping calculation""" return weight * 2.5 # $2.5 per kg
def get_info(self) -> str: return f"{self.name} - ${self.price:.2f} (SKU: {self.sku})"
class DigitalProduct(Product): """Digital products - no shipping""" def __init__(self, name: str, price: float, sku: str, download_size: float): super().__init__(name, price, sku) self.download_size = download_size # in MB
def calculate_shipping(self, weight: float) -> float: """Override - digital products have no shipping cost""" return 0.0
def get_info(self) -> str: base_info = super().get_info() return f"{base_info} - Digital Download ({self.download_size} MB)"
class PhysicalProduct(Product): """Physical products with weight""" def __init__(self, name: str, price: float, sku: str, weight: float): super().__init__(name, price, sku) self.weight = weight # in kg
def calculate_shipping(self, weight: float = None) -> float: """Override - use product's own weight""" actual_weight = weight if weight else self.weight return super().calculate_shipping(actual_weight)
def get_info(self) -> str: base_info = super().get_info() return f"{base_info} - Weight: {self.weight} kg"
class FragileProduct(PhysicalProduct): """Fragile products need special handling""" def __init__(self, name: str, price: float, sku: str, weight: float): super().__init__(name, price, sku, weight)
def calculate_shipping(self, weight: float = None) -> float: """Override - add handling fee for fragile items""" base_shipping = super().calculate_shipping(weight) handling_fee = 10.0 # Additional $10 for fragile items return base_shipping + handling_fee
def get_info(self) -> str: base_info = super().get_info() return f"{base_info} - FRAGILE"
# Usageebook = DigitalProduct("Python Guide", 29.99, "DIG-001", 5.2)print(ebook.get_info())print(f"Shipping: ${ebook.calculate_shipping(0):.2f}")
book = PhysicalProduct("Python Book", 39.99, "PHY-001", 0.5)print(book.get_info())print(f"Shipping: ${book.calculate_shipping():.2f}")
vase = FragileProduct("Ceramic Vase", 49.99, "FRG-001", 1.2)print(vase.get_info())print(f"Shipping: ${vase.calculate_shipping():.2f}")// Base class for all productspublic class Product { protected String name; protected double price; protected String sku;
public Product(String name, double price, String sku) { this.name = name; this.price = price; this.sku = sku; }
// Base shipping calculation public double calculateShipping(double weight) { return weight * 2.5; // $2.5 per kg }
public String getInfo() { return String.format("%s - $%.2f (SKU: %s)", name, price, sku); }}
// Digital products - no shippingpublic class DigitalProduct extends Product { private double downloadSize; // in MB
public DigitalProduct(String name, double price, String sku, double downloadSize) { super(name, price, sku); this.downloadSize = downloadSize; }
@Override public double calculateShipping(double weight) { // Override - digital products have no shipping cost return 0.0; }
@Override public String getInfo() { String baseInfo = super.getInfo(); return baseInfo + " - Digital Download (" + downloadSize + " MB)"; }}
// Physical products with weightpublic class PhysicalProduct extends Product { private double weight; // in kg
public PhysicalProduct(String name, double price, String sku, double weight) { super(name, price, sku); this.weight = weight; }
public double calculateShipping() { // Override - use product's own weight return calculateShipping(weight); }
@Override public double calculateShipping(double weight) { // Use provided weight or product's own weight double actualWeight = weight > 0 ? weight : this.weight; return super.calculateShipping(actualWeight); }
@Override public String getInfo() { String baseInfo = super.getInfo(); return baseInfo + " - Weight: " + weight + " kg"; }}
// Fragile products need special handlingpublic class FragileProduct extends PhysicalProduct { public FragileProduct(String name, double price, String sku, double weight) { super(name, price, sku, weight); }
@Override public double calculateShipping(double weight) { // Override - add handling fee for fragile items double baseShipping = super.calculateShipping(weight); double handlingFee = 10.0; // Additional $10 for fragile items return baseShipping + handlingFee; }
@Override public String getInfo() { String baseInfo = super.getInfo(); return baseInfo + " - FRAGILE"; }}
// Usagepublic class Main { public static void main(String[] args) { DigitalProduct ebook = new DigitalProduct("Python Guide", 29.99, "DIG-001", 5.2); System.out.println(ebook.getInfo()); System.out.printf("Shipping: $%.2f%n", ebook.calculateShipping(0));
PhysicalProduct book = new PhysicalProduct("Python Book", 39.99, "PHY-001", 0.5); System.out.println(book.getInfo()); System.out.printf("Shipping: $%.2f%n", book.calculateShipping());
FragileProduct vase = new FragileProduct("Ceramic Vase", 49.99, "FRG-001", 1.2); System.out.println(vase.getInfo()); System.out.printf("Shipping: $%.2f%n", vase.calculateShipping()); }}Multiple Inheritance
Section titled “Multiple Inheritance”class Flyable: def fly(self): return "Flying through the air"
class Swimmable: def swim(self): return "Swimming in water"
class Duck(Flyable, Swimmable): """Duck can both fly and swim""" def __init__(self, name: str): self.name = name
def quack(self): return f"{self.name} says quack!"
duck = Duck("Donald")print(duck.fly()) # From Flyableprint(duck.swim()) # From Swimmableprint(duck.quack()) # Duck's own method// Java uses interfaces for multiple inheritance-like behaviorpublic interface Flyable { default void fly() { System.out.println("Flying through the air"); }}
public interface Swimmable { default void swim() { System.out.println("Swimming in water"); }}
// Duck implements multiple interfaces (similar to multiple inheritance)public class Duck implements Flyable, Swimmable { private String name;
public Duck(String name) { this.name = name; }
public void quack() { System.out.println(name + " says quack!"); }}
// Usagepublic class Main { public static void main(String[] args) { Duck duck = new Duck("Donald"); duck.fly(); // From Flyable interface duck.swim(); // From Swimmable interface duck.quack(); // Duck's own method }}Note: Java 8+ introduced default methods in interfaces, allowing interfaces to provide method implementations. This gives you multiple inheritance-like behavior while avoiding the diamond problem.
Method Resolution Order (MRO)
Section titled “Method Resolution Order (MRO)”When using multiple inheritance, Python uses Method Resolution Order to determine which method to call:
class A: def method(self): return "A"
class B(A): def method(self): return "B"
class C(A): def method(self): return "C"
class D(B, C): pass
d = D()print(d.method()) # "B" - B comes before C in MROprint(D.__mro__) # Shows the method resolution order// Java uses single inheritance, so no MRO complexitypublic class A { public String method() { return "A"; }}
public class B extends A { @Override public String method() { return "B"; }}
// Java doesn't allow: class D extends B, C// Instead, you use interfaces for multiple inheritance-like behavior
public interface Interface1 { default String method() { return "Interface1"; }}
public interface Interface2 { default String method() { return "Interface2"; }}
// If both interfaces have the same method, class must overridepublic class D implements Interface1, Interface2 { @Override public String method() { // Must override to resolve conflict return "D resolves conflict"; }}
// Usagepublic class Main { public static void main(String[] args) { B b = new B(); System.out.println(b.method()); // "B" - simple override
D d = new D(); System.out.println(d.method()); // "D resolves conflict" - must override }}Key Difference: Python’s MRO handles multiple inheritance automatically, while Java requires explicit resolution when interfaces conflict.
Key Takeaways
Section titled “Key Takeaways”- Inheritance creates an “is-a” relationship between classes
- Subclasses inherit all attributes and methods from parent classes
- Method overriding allows subclasses to provide specialized behavior
super()is used to call parent class methods- Multiple inheritance allows a class to inherit from multiple parents
- MRO determines the order in which methods are resolved in multiple inheritance
- Use inheritance to reuse code and create specialized classes from general ones
Inheritance is powerful for creating class hierarchies where specialized classes extend and customize the behavior of more general classes.