Day 3へようこそ!本日は、コードを重複させることなくクラス間で振る舞いを共有、拡張、適応させる方法を探ります。
- 継承: 既存のクラスを基に新しいクラスを作成し、コードを再利用して階層構造を確立します。
-
メソッドのオーバーライドと
super(): 子クラスで親クラスの振る舞いを再定義しつつ、親クラスの実装へのアクセスを保持します。 - ポリモーフィズムとダックタイピング: 共通のインターフェースやメソッドセットを通じて、異なるオブジェクト型を操作します。
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
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.