mirror of
https://github.com/blshaer/python-by-example.git
synced 2026-03-27 23:29:25 +01:00
437 lines
12 KiB
Python
437 lines
12 KiB
Python
"""
|
|
================================================================================
|
|
File: 03_inheritance.py
|
|
Topic: Inheritance in Python
|
|
================================================================================
|
|
|
|
This file demonstrates inheritance in Python, a fundamental OOP concept that
|
|
allows classes to inherit attributes and methods from other classes. This
|
|
promotes code reuse and establishes relationships between classes.
|
|
|
|
Key Concepts:
|
|
- Single inheritance
|
|
- Method overriding
|
|
- super() function
|
|
- Multiple inheritance
|
|
- Method Resolution Order (MRO)
|
|
- Abstract base classes
|
|
|
|
================================================================================
|
|
"""
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 1. Basic Inheritance
|
|
# -----------------------------------------------------------------------------
|
|
# Child class inherits from parent class
|
|
|
|
print("--- Basic Inheritance ---")
|
|
|
|
class Animal:
|
|
"""Base class for all animals."""
|
|
|
|
def __init__(self, name):
|
|
"""Initialize animal with a name."""
|
|
self.name = name
|
|
|
|
def speak(self):
|
|
"""Make a generic sound."""
|
|
return "Some generic sound"
|
|
|
|
def describe(self):
|
|
"""Describe the animal."""
|
|
return f"I am {self.name}"
|
|
|
|
class Dog(Animal):
|
|
"""Dog class inheriting from Animal."""
|
|
pass # Inherits everything from Animal
|
|
|
|
# Dog has all Animal's methods
|
|
buddy = Dog("Buddy")
|
|
print(buddy.describe())
|
|
print(f"Says: {buddy.speak()}")
|
|
print(f"Dog is instance of Animal: {isinstance(buddy, Animal)}")
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 2. Method Overriding
|
|
# -----------------------------------------------------------------------------
|
|
# Child class can override parent methods
|
|
|
|
print("\n--- Method Overriding ---")
|
|
|
|
class Cat(Animal):
|
|
"""Cat class with overridden method."""
|
|
|
|
def speak(self):
|
|
"""Cats meow instead of generic sound."""
|
|
return "Meow!"
|
|
|
|
class Cow(Animal):
|
|
"""Cow class with overridden method."""
|
|
|
|
def speak(self):
|
|
"""Cows moo."""
|
|
return "Moo!"
|
|
|
|
# Each animal speaks differently
|
|
animals = [Dog("Rex"), Cat("Whiskers"), Cow("Bessie")]
|
|
|
|
print("Each animal speaks:")
|
|
for animal in animals:
|
|
print(f" {animal.name}: {animal.speak()}")
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 3. Extending Parent Class with super()
|
|
# -----------------------------------------------------------------------------
|
|
# Use super() to call parent methods
|
|
|
|
print("\n--- Using super() ---")
|
|
|
|
class Bird(Animal):
|
|
"""Bird class extending Animal."""
|
|
|
|
def __init__(self, name, can_fly=True):
|
|
"""Initialize bird with flying ability."""
|
|
super().__init__(name) # Call parent's __init__
|
|
self.can_fly = can_fly
|
|
|
|
def speak(self):
|
|
"""Birds chirp."""
|
|
return "Chirp!"
|
|
|
|
def describe(self):
|
|
"""Extend parent's describe method."""
|
|
base = super().describe() # Get parent's description
|
|
fly_status = "can fly" if self.can_fly else "cannot fly"
|
|
return f"{base} and I {fly_status}"
|
|
|
|
sparrow = Bird("Sparrow", can_fly=True)
|
|
penguin = Bird("Penny", can_fly=False)
|
|
|
|
print(sparrow.describe())
|
|
print(penguin.describe())
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 4. Inheritance Chain
|
|
# -----------------------------------------------------------------------------
|
|
# Classes can inherit from other child classes
|
|
|
|
print("\n--- Inheritance Chain ---")
|
|
|
|
class Vehicle:
|
|
"""Base vehicle class."""
|
|
|
|
def __init__(self, brand):
|
|
self.brand = brand
|
|
|
|
def start(self):
|
|
return "Vehicle starting..."
|
|
|
|
class Car(Vehicle):
|
|
"""Car inherits from Vehicle."""
|
|
|
|
def __init__(self, brand, model):
|
|
super().__init__(brand)
|
|
self.model = model
|
|
|
|
def start(self):
|
|
return f"{self.brand} {self.model} engine starting..."
|
|
|
|
class ElectricCar(Car):
|
|
"""ElectricCar inherits from Car."""
|
|
|
|
def __init__(self, brand, model, battery_kw):
|
|
super().__init__(brand, model)
|
|
self.battery_kw = battery_kw
|
|
|
|
def start(self):
|
|
return f"{self.brand} {self.model} powering up silently... (Battery: {self.battery_kw}kW)"
|
|
|
|
def charge(self):
|
|
return f"Charging {self.battery_kw}kW battery..."
|
|
|
|
# Create instances
|
|
regular_car = Car("Toyota", "Camry")
|
|
electric_car = ElectricCar("Tesla", "Model 3", 75)
|
|
|
|
print(regular_car.start())
|
|
print(electric_car.start())
|
|
print(electric_car.charge())
|
|
|
|
# Check inheritance
|
|
print(f"\nElectricCar is Car: {isinstance(electric_car, Car)}")
|
|
print(f"ElectricCar is Vehicle: {isinstance(electric_car, Vehicle)}")
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 5. Multiple Inheritance
|
|
# -----------------------------------------------------------------------------
|
|
# A class can inherit from multiple parent classes
|
|
|
|
print("\n--- Multiple Inheritance ---")
|
|
|
|
class Flyable:
|
|
"""Mixin class for flying ability."""
|
|
|
|
def fly(self):
|
|
return f"{self.name} is flying!"
|
|
|
|
def land(self):
|
|
return f"{self.name} has landed."
|
|
|
|
class Swimmable:
|
|
"""Mixin class for swimming ability."""
|
|
|
|
def swim(self):
|
|
return f"{self.name} is swimming!"
|
|
|
|
def dive(self):
|
|
return f"{self.name} dives underwater."
|
|
|
|
class Duck(Animal, Flyable, Swimmable):
|
|
"""Duck can do everything!"""
|
|
|
|
def speak(self):
|
|
return "Quack!"
|
|
|
|
# Duck has methods from all parent classes
|
|
duck = Duck("Donald")
|
|
print(f"{duck.name} says: {duck.speak()}")
|
|
print(duck.fly())
|
|
print(duck.swim())
|
|
print(duck.describe())
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 6. Method Resolution Order (MRO)
|
|
# -----------------------------------------------------------------------------
|
|
# Python determines method lookup order
|
|
|
|
print("\n--- Method Resolution Order (MRO) ---")
|
|
|
|
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
|
|
|
|
class E(B, C):
|
|
def method(self):
|
|
return "E"
|
|
|
|
# Check MRO
|
|
print("MRO for class D:")
|
|
for cls in D.__mro__:
|
|
print(f" {cls.__name__}")
|
|
|
|
d = D()
|
|
e = E()
|
|
print(f"\nd.method(): {d.method()}") # Returns "B" (first in MRO after D)
|
|
print(f"e.method(): {e.method()}") # Returns "E" (overridden)
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 7. Calling Methods from Specific Parent
|
|
# -----------------------------------------------------------------------------
|
|
|
|
print("\n--- Calling Specific Parent Methods ---")
|
|
|
|
class Parent1:
|
|
def greet(self):
|
|
return "Hello from Parent1"
|
|
|
|
class Parent2:
|
|
def greet(self):
|
|
return "Hello from Parent2"
|
|
|
|
class Child(Parent1, Parent2):
|
|
def greet(self):
|
|
return "Hello from Child"
|
|
|
|
def greet_all(self):
|
|
"""Call all parent greet methods."""
|
|
return [
|
|
f"Child: {self.greet()}",
|
|
f"Parent1: {Parent1.greet(self)}",
|
|
f"Parent2: {Parent2.greet(self)}"
|
|
]
|
|
|
|
child = Child()
|
|
print("Calling all greet methods:")
|
|
for greeting in child.greet_all():
|
|
print(f" {greeting}")
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 8. Abstract Base Classes
|
|
# -----------------------------------------------------------------------------
|
|
# Define interfaces that must be implemented
|
|
|
|
print("\n--- Abstract Base Classes ---")
|
|
|
|
from abc import ABC, abstractmethod
|
|
|
|
class Shape(ABC):
|
|
"""Abstract base class for shapes."""
|
|
|
|
@abstractmethod
|
|
def area(self):
|
|
"""Calculate area - must be implemented."""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def perimeter(self):
|
|
"""Calculate perimeter - must be implemented."""
|
|
pass
|
|
|
|
def describe(self):
|
|
"""Non-abstract method - can be used as-is."""
|
|
return f"A shape with area {self.area():.2f}"
|
|
|
|
class Rectangle(Shape):
|
|
"""Concrete rectangle class."""
|
|
|
|
def __init__(self, width, height):
|
|
self.width = width
|
|
self.height = height
|
|
|
|
def area(self):
|
|
return self.width * self.height
|
|
|
|
def perimeter(self):
|
|
return 2 * (self.width + self.height)
|
|
|
|
class Circle(Shape):
|
|
"""Concrete circle class."""
|
|
|
|
def __init__(self, radius):
|
|
self.radius = radius
|
|
|
|
def area(self):
|
|
import math
|
|
return math.pi * self.radius ** 2
|
|
|
|
def perimeter(self):
|
|
import math
|
|
return 2 * math.pi * self.radius
|
|
|
|
# Cannot instantiate abstract class
|
|
# shape = Shape() # TypeError!
|
|
|
|
# Concrete classes work fine
|
|
rect = Rectangle(5, 3)
|
|
circle = Circle(4)
|
|
|
|
print(f"Rectangle: area={rect.area()}, perimeter={rect.perimeter()}")
|
|
print(f"Circle: area={circle.area():.2f}, perimeter={circle.perimeter():.2f}")
|
|
print(rect.describe())
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 9. Using isinstance() and issubclass()
|
|
# -----------------------------------------------------------------------------
|
|
|
|
print("\n--- Type Checking ---")
|
|
|
|
class Mammal(Animal):
|
|
pass
|
|
|
|
class Reptile(Animal):
|
|
pass
|
|
|
|
class DogV2(Mammal):
|
|
def speak(self):
|
|
return "Woof!"
|
|
|
|
class Snake(Reptile):
|
|
def speak(self):
|
|
return "Hiss!"
|
|
|
|
dog = DogV2("Rex")
|
|
snake = Snake("Slinky")
|
|
|
|
# isinstance() checks if object is instance of class
|
|
print("isinstance checks:")
|
|
print(f" dog isinstance of DogV2: {isinstance(dog, DogV2)}")
|
|
print(f" dog isinstance of Mammal: {isinstance(dog, Mammal)}")
|
|
print(f" dog isinstance of Animal: {isinstance(dog, Animal)}")
|
|
print(f" dog isinstance of Reptile: {isinstance(dog, Reptile)}")
|
|
|
|
# issubclass() checks class relationships
|
|
print("\nissubclass checks:")
|
|
print(f" DogV2 issubclass of Mammal: {issubclass(DogV2, Mammal)}")
|
|
print(f" DogV2 issubclass of Animal: {issubclass(DogV2, Animal)}")
|
|
print(f" DogV2 issubclass of Reptile: {issubclass(DogV2, Reptile)}")
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# 10. Practical Example: Employee Hierarchy
|
|
# -----------------------------------------------------------------------------
|
|
|
|
print("\n--- Practical Example: Employee Hierarchy ---")
|
|
|
|
class Employee:
|
|
"""Base employee class."""
|
|
|
|
def __init__(self, name, employee_id, salary):
|
|
self.name = name
|
|
self.employee_id = employee_id
|
|
self._salary = salary
|
|
|
|
@property
|
|
def salary(self):
|
|
return self._salary
|
|
|
|
def get_annual_salary(self):
|
|
return self._salary * 12
|
|
|
|
def __str__(self):
|
|
return f"{self.name} (ID: {self.employee_id})"
|
|
|
|
class Manager(Employee):
|
|
"""Manager with team and bonus."""
|
|
|
|
def __init__(self, name, employee_id, salary, team_size=0):
|
|
super().__init__(name, employee_id, salary)
|
|
self.team_size = team_size
|
|
|
|
def get_annual_salary(self):
|
|
# Managers get bonus based on team size
|
|
base = super().get_annual_salary()
|
|
bonus = self.team_size * 1000 # $1000 per team member
|
|
return base + bonus
|
|
|
|
def __str__(self):
|
|
return f"Manager {super().__str__()} - Team: {self.team_size}"
|
|
|
|
class Developer(Employee):
|
|
"""Developer with tech stack."""
|
|
|
|
def __init__(self, name, employee_id, salary, languages):
|
|
super().__init__(name, employee_id, salary)
|
|
self.languages = languages
|
|
|
|
def get_annual_salary(self):
|
|
# Developers get extra for each language
|
|
base = super().get_annual_salary()
|
|
language_bonus = len(self.languages) * 500 # $500 per language
|
|
return base + language_bonus
|
|
|
|
def __str__(self):
|
|
langs = ", ".join(self.languages)
|
|
return f"Developer {super().__str__()} - Languages: {langs}"
|
|
|
|
# Create employees
|
|
manager = Manager("Alice", "M001", 8000, team_size=5)
|
|
dev1 = Developer("Bob", "D001", 6000, ["Python", "JavaScript", "SQL"])
|
|
dev2 = Developer("Charlie", "D002", 5500, ["Python"])
|
|
|
|
employees = [manager, dev1, dev2]
|
|
|
|
print("Employee Details:")
|
|
for emp in employees:
|
|
print(f" {emp}")
|
|
print(f" Annual Salary: ${emp.get_annual_salary():,}")
|