Day 3へようこそ!本日は、コードを重複させることなくクラス間で振る舞いを共有、拡張、適応させる方法を探ります。

  1. 継承: 既存のクラスを基に新しいクラスを作成し、コードを再利用して階層構造を確立します。
  2. メソッドのオーバーライドとsuper() 子クラスで親クラスの振る舞いを再定義しつつ、親クラスの実装へのアクセスを保持します。
  3. ポリモーフィズムとダックタイピング: 共通のインターフェースやメソッドセットを通じて、異なるオブジェクト型を操作します。

1. 継承の構造 🌳

Pythonは主に3種類の継承をサポートします:

  SINGLE INHERITANCE         MULTILEVEL INHERITANCE        MULTIPLE INHERITANCE

     ┌──────────┐                 ┌──────────┐           ┌───────┐   ┌─────────┐
     │  Parent  │                 │  Grandpa │           │ Mixin │   │ BaseClass│
     └────┬─────┘                 └────┬─────┘           └───┬───┘   └───┬─────┘
          │                            │                     │           │
          ▼                            ▼                     └─────┬─────┘
     ┌──────────┐                 ┌──────────┐                     ▼
     │  Child   │                 │  Parent  │               ┌───────────┐
     └──────────┘                 └────┬─────┘               │   Child   │
                                       │                     └───────────┘
                                       ▼
                                  ┌──────────┐
                                  │  Child   │
                                  └──────────┘

Enter fullscreen mode Exit fullscreen mode

  • 単一継承: 1つの子クラスが1つの親クラスから直接継承します。
  • 多段階継承: 子クラスが親クラスから継承し、その親クラス自体が祖父母クラスから継承する(連鎖)。
  • 多重継承: 1つの子クラスが2つ以上の親クラスから直接継承します。

2. メソッドのオーバーライドとsuper() 🔄

  • メソッドのオーバーライド: 子クラスが親クラス内のメソッドと全く同じ名前のメソッドを定義したときに発生します。子クラスのメソッドが親クラスの振る舞いを置き換えたり変更したりします。
  • super() 子クラス内で親クラスのメソッドを呼び出すことを可能にする組み込み関数です。親クラスの機能を拡張する際にコードの重複を防ぎます。

🌱 クイック例

class Animal:
    def __init__(self, name: str):
        self.name = name

    def make_sound(self) -> str:
        return "Generic animal sound"

class Dog(Animal):
    def __init__(self, name: str, breed: str):
        # Call parent constructor using super()
        super().__init__(name)
        self.breed = breed

    # Override parent method
    def make_sound(self) -> str:
        parent_sound = super().make_sound()
        return f"{parent_sound} -> Woof! 🐾"

dog = Dog("Rex", "German Shepherd")
print(dog.make_sound())  # Output: Generic animal sound -> Woof! 🐾

Enter fullscreen mode Exit fullscreen mode


3. ポリモーフィズムとダックタイピング 🦆

ポリモーフィズム(「多様な形状」)により、異なるクラスが同じメソッド呼び出しに対してそれぞれ独自の方法で応答できるようになります。

Pythonでは、ポリモーフィズムはダックタイピングと密接に関連しています:

「アヒルのように歩き、アヒルのように鳴くなら、それはアヒルだ。」

Pythonは明示的なクラス階層や明示的なインターフェース契約に関心がありません。オブジェクトが期待されるメソッド(例:.calculate_pay())を実装していれば、Pythonは喜んでそれを実行します。

                     ┌───────────────────────────────┐
                     │   Payroll Processor Service   │
                     └───────────────┬───────────────┘
                                     │
                      Calls .calculate_pay() on each
                                     │
      ┌──────────────────────────────┼──────────────────────────────┐
      ▼                              ▼                              ▼
┌──────────┐                   ┌──────────┐                   ┌──────────┐
│ FullTime │                   │ Executive│                   │Contractor│
│ Employee │                   │ Manager  │                   │ (No Base)│
└──────────┘                   └──────────┘                   └──────────┘

Enter fullscreen mode Exit fullscreen mode


4. 演習課題:従業員管理システムの構築 🏢

以下は、単一継承、多段階継承、多重継承、super()を用いたメソッドのオーバーライド、および統一されたPayrollProcessorを通じたダックタイピングを示す、エンタープライズ対応の従業員管理システムです。

ステップ1:ワークスペースの初期化

uv init oop_day3 && cd oop_day3
touch employee_system.py

Enter fullscreen mode Exit fullscreen mode

ステップ2:employee_system.pyの実装

# employee_system.py
from typing import List, Any


# ==========================================
# 1. BASE CLASS (PARENT)
# ==========================================
class Employee:
    """Base class for all salaried employees."""

    def __init__(self, emp_id: str, name: str, base_salary: float):
        self.emp_id = emp_id
        self.name = name
        self.base_salary = base_salary

    def calculate_pay(self) -> float:
        """Calculates standard monthly pay."""
        return round(self.base_salary, 2)

    def get_details(self) -> str:
        return f"[{self.emp_id}] {self.name} | Role: Employee | Base Salary: ${self.base_salary:,.2f}"


# ==========================================
# 2. SINGLE INHERITANCE: DEVELOPER
# ==========================================
class Developer(Employee):
    """Inherits directly from Employee (Single Inheritance)."""

    def __init__(self, emp_id: str, name: str, base_salary: float, tech_stack: str):
        super().__init__(emp_id, name, base_salary)
        self.tech_stack = tech_stack

    # Override calculate_pay to add a tech allowance
    def calculate_pay(self) -> float:
        tech_bonus = 300.00  # Fixed monthly tooling stipend
        return round(super().calculate_pay() + tech_bonus, 2)

    def get_details(self) -> str:
        return f"[{self.emp_id}] {self.name} | Role: Developer ({self.tech_stack})"


# ==========================================
# 3. MULTILEVEL INHERITANCE: MANAGER -> EXECUTIVE
# ==========================================
class Manager(Employee):
    """Inherits from Employee (Level 1)."""

    def __init__(self, emp_id: str, name: str, base_salary: float, team_size: int):
        super().__init__(emp_id, name, base_salary)
        self.team_size = team_size

    def calculate_pay(self) -> float:
        team_allowance = self.team_size * 150.00
        return round(super().calculate_pay() + team_allowance, 2)

    def get_details(self) -> str:
        return f"[{self.emp_id}] {self.name} | Role: Manager | Team Size: {self.team_size}"


class Executive(Manager):
    """Inherits from Manager (Level 2 - Multilevel Inheritance: Employee -> Manager -> Executive)."""

    def __init__(self, emp_id: str, name: str, base_salary: float, team_size: int, stock_options: int):
        super().__init__(emp_id, name, base_salary, team_size)
        self.stock_options = stock_options

    def calculate_pay(self) -> float:
        executive_bonus = 2500.00
        # Combines base salary + team allowance (from Manager) + executive bonus
        return round(super().calculate_pay() + executive_bonus, 2)

    def get_details(self) -> str:
        return f"[{self.emp_id}] {self.name} | Role: Executive | Options: {self.stock_options} units"


# ==========================================
# 4. MULTIPLE INHERITANCE / MIXIN: AUDITABLE
# ==========================================
class ComplianceAuditableMixin:
    """A standalone mixin class that adds auditing capabilities."""

    def log_audit(self, action: str) -> None:
        print(f"[AUDIT LOG] Entity Action Recorded: '{action}'")


class TechLead(Developer, ComplianceAuditableMixin):
    """Inherits from Developer AND ComplianceAuditableMixin (Multiple Inheritance)."""

    def __init__(self, emp_id: str, name: str, base_salary: float, tech_stack: str, projects_count: int):
        Developer.__init__(self, emp_id, name, base_salary, tech_stack)
        self.projects_count = projects_count

    def get_details(self) -> str:
        return f"[{self.emp_id}] {self.name} | Role: Tech Lead | Projects: {self.projects_count}"


# ==========================================
# 5. DUCK TYPING: CONTRACTOR (NO BASE CLASS)
# ==========================================
class ExternalContractor:
    """Does NOT inherit from Employee, but implements calculate_pay() and get_details()."""

    def __init__(self, contractor_id: str, name: str, hourly_rate: float, hours_worked: float):
        self.contractor_id = contractor_id
        self.name = name
        self.hourly_rate = hourly_rate
        self.hours_worked = hours_worked

    def calculate_pay(self) -> float:
        return round(self.hourly_rate * self.hours_worked, 2)

    def get_details(self) -> str:
        return f"[{self.contractor_id}] {self.name} | External Contractor ({self.hours_worked} hrs @ ${self.hourly_rate}/hr)"


# ==========================================
# 6. UNIFIED PAYROLL PROCESSOR (POLYMORPHISM)
# ==========================================
class PayrollProcessor:
    """Uses Polymorphism & Duck Typing to process pay across any compatible worker object."""

    @staticmethod
    def process_payroll(workers: List[Any]) -> None:
        print("==========================================================")
        print("             MONTHLY PAYROLL REPORT                       ")
        print("==========================================================")
        total_payout = 0.0

        for worker in workers:
            # Duck typing in action: as long as worker has get_details() and calculate_pay(), it works
            details = worker.get_details()
            pay = worker.calculate_pay()
            total_payout += pay
            print(f"{details}\n  └─ Calculated Payout: ${pay:,.2f}\n")

        print("----------------------------------------------------------")
        print(f" TOTAL SYSTEM PAYOUT: ${total_payout:,.2f}")
        print("==========================================================\n")


# ==========================================
# TEST EXECUTION SUITE
# ==========================================
if __name__ == "__main__":
    # Create instances across various inheritance patterns
    dev = Developer("EMP-101", "Alice Chen", base_salary=7500.00, tech_stack="Python/FastAPI")
    mgr = Manager("EMP-102", "Bob Smith", base_salary=9000.00, team_size=5)
    exec_officer = Executive("EMP-103", "Carol Danvers", base_salary=15000.00, team_size=20, stock_options=5000)

    # Multiple Inheritance instance
    tech_lead = TechLead("EMP-104", "David Miller", base_salary=11000.00, tech_stack="Distributed Systems", projects_count=3)
    tech_lead.log_audit("Approved Architecture Review")  # Mixin functionality
    # Duck Typing instance (No inheritance from Employee class)
    contractor = ExternalContractor("CONT-801", "Eva Green", hourly_rate=85.00, hours_worked=120)

    # Heterogeneous collection passed into PayrollProcessor
    workforce = [dev, mgr, exec_officer, tech_lead, contractor]

    # Run Polymorphic Payroll
    PayrollProcessor.process_payroll(workforce)

Enter fullscreen mode Exit fullscreen mode

ステップ3:実行と検証

uv run employee_system.py

Enter fullscreen mode Exit fullscreen mode

出力サマリー

[AUDIT LOG] Entity Action Recorded: 'Approved Architecture Review'

==========================================================
             MONTHLY PAYROLL REPORT                       
==========================================================
[EMP-101] Alice Chen | Role: Developer (Python/FastAPI)
  └─ Calculated Payout: $7,800.00

[EMP-102] Bob Smith | Role: Manager | Team Size: 5
  └─ Calculated Payout: $9,750.00

[EMP-103] Carol Danvers | Role: Executive | Options: 5000 units
  └─ Calculated Payout: $20,500.00

[EMP-104] David Miller | Role: Tech Lead | Projects: 3
  └─ Calculated Payout: $11,300.00

[CONT-801] Eva Green | External Contractor (120 hrs @ $85.0/hr)
  └─ Calculated Payout: $10,200.00

----------------------------------------------------------
 TOTAL SYSTEM PAYOUT: $59,550.00
==========================================================

Enter fullscreen mode Exit fullscreen mode