Python 継承とポリモーフィズム
2025-10-27はじめに
今回はオブジェクト指向プログラミングの「継承」と「ポリモーフィズム」について詳しく探っていきます。これらの概念は、コードの再利用性を高め、柔軟で拡張性の高いプログラムを構築するための強力なツールです。
現実世界で例えるなら、継承は「親から子へ特徴が受け継がれる」関係に似ています。例えば、「自動車」という基本クラスがあり、そこから「スポーツカー」「SUV」「トラック」などの特殊なクラスが派生するようなイメージです。
継承の基本
継承とは、既存のクラスを基にして新しいクラスを作成する仕組みです。基になるクラスを「親クラス」「スーパークラス」「基底クラス」と呼び、新しく作成するクラスを「子クラス」「サブクラス」「派生クラス」と呼びます。

次のコードは 継承とメソッドのオーバーライド の基本例です。
Animal クラスを親クラスとして定義し、Dog と Cat クラスがそれを継承しています。
共通の属性(name, age)やメソッド(sleep())は親クラスから引き継がれ、それぞれのクラスで speak() メソッドをオーバーライド(上書き) して、犬と猫に特有の鳴き声を出すように動作を変更しています。
class Animal:
def __init__(self, name, age):
self.name = name
self.age = age
def speak(self):
print("動物の鳴き声")
def sleep(self):
print(f"{self.name}は眠っています")
class Dog(Animal): # Animalクラスを継承
def __init__(self, name, age, breed):
super().__init__(name, age) # 親クラスのコンストラクタを呼び出し
self.breed = breed
def speak(self): # メソッドのオーバーライド
print(f"{self.name}はワンワン吠えます")
class Cat(Animal): # Animalクラスを継承
def __init__(self, name, age, color):
super().__init__(name, age)
self.color = color
def speak(self): # メソッドのオーバーライド
print(f"{self.name}はニャーと鳴きます")
継承の利点
- コードの再利用: 親クラスの機能をそのまま利用できる
- 保守性の向上: 共通機能を一箇所で管理できる
- 拡張性: 新しい機能を簡単に追加できる
- 階層的な構造: 現実世界の関係を自然に表現できる
super()関数の役割
super()関数は、親クラスのメソッドを呼び出すための重要な関数です。特にコンストラクタで親クラスの初期化を行う際に頻繁に使用されます。
class Vehicle:
def __init__(self, brand, model, year):
self.brand = brand
self.model = model
self.year = year
self.speed = 0
def start_engine(self):
print("エンジンを始動しました")
def stop_engine(self):
print("エンジンを停止しました")
class Car(Vehicle):
def __init__(self, brand, model, year, doors):
super().__init__(brand, model, year) # 親クラスのコンストラクタを呼び出し
self.doors = doors # 子クラス独自の属性
def open_trunk(self): # 子クラス独自のメソッド
print("トランクを開けました")
# 使用例
my_car = Car("Toyota", "Camry", 2023, 4)
print(f"{my_car.brand} {my_car.model}") # 親クラスの属性にアクセス
my_car.start_engine() # 親クラスのメソッドを呼び出し
Vehicleクラスが共通の属性(ブランド、モデル、年式)や動作(エンジンの始動・停止)を定義し、Carクラスがそれを継承して自動車特有の属性doorsやメソッドopen_trunk()を追加しています。これにより、共通の機能を親クラスにまとめつつ、子クラスで独自の機能を拡張できる仕組みが理解できます
メソッドのオーバーライド
オーバーライドとは、親クラスで定義されたメソッドを子クラスで再定義することです。これにより、同じメソッド名でも異なる振る舞いを実現できます。オーバーライドにより、同じメソッド名でもクラスごとに異なる動作を実現でき、ポリモーフィズム(多態性)が成立します。以下のコードの例を参照します。
class Shape:
def __init__(self, name):
self.name = name
def area(self):
raise NotImplementedError("サブクラスで実装してください")
def perimeter(self):
raise NotImplementedError("サブクラスで実装してください")
def display_info(self):
print(f"図形: {self.name}")
class Rectangle(Shape):
def __init__(self, width, height):
super().__init__("長方形")
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):
def __init__(self, radius):
super().__init__("円")
self.radius = radius
def area(self): # オーバーライド
return 3.14159 * self.radius ** 2
def perimeter(self): # オーバーライド
return 2 * 3.14159 * self.radius
親クラスShapeで定義されたarea()やperimeter()を、子クラスRectangleやCircleがそれぞれの図形に合わせて再定義することで、同じメソッド名でも異なる処理を実現しています。これにより、ポリモーフィズムが成立し、例えばshapes = [Rectangle(3, 4), Circle(5)]のように異なる図形をまとめて扱っても、shape.area()の呼び出しだけで各図形に適した面積が計算されます。結果として、柔軟で拡張性の高いコード設計が可能になります。
ポリモーフィズムの概念
ポリモーフィズム(多態性)とは、同じインターフェースで異なる振る舞いを実現する仕組みです。異なるクラスのオブジェクトが同じメソッド名で呼び出されても、各クラスに適した動作を行うことができます。
ポリモーフィズムの実例
Birdクラスを基底に、Sparrow・Eagle・Penguinがそれぞれfly()メソッドを独自に定義しています。make_bird_fly()関数は同じfly()呼び出しを行いますが、実際の動作はオブジェクトの種類に応じて異なる結果を出力します。
class Bird:
def fly(self):
pass
class Sparrow(Bird):
def fly(self):
print("スズメはバサバサ飛びます")
class Eagle(Bird):
def fly(self):
print("ワシは悠然と飛びます")
class Penguin(Bird):
def fly(self):
print("ペンギンは飛べません")
def make_bird_fly(bird):
bird.fly() # 同じメソッド呼び出しでも、オブジェクトによって異なる動作
# 使用例
birds = [Sparrow(), Eagle(), Penguin()]
for bird in birds:
make_bird_fly(bird)
出力結果:
スズメはバサバサ飛びます
ワシは悠然と飛びます
ペンギンは飛べません
多重継承
Pythonでは、複数の親クラスから継承する「多重継承」が可能です。これは強力な機能ですが、注意して使用する必要があります。
class Flyable:
def fly(self):
print("飛んでいます")
class Swimmable:
def swim(self):
print("泳いでいます")
class Duck(Flyable, Swimmable):
def __init__(self, name):
self.name = name
def quack(self):
print(f"{self.name}がガーガー鳴きます")
# 使用例
duck = Duck("ドナルド")
duck.fly() # Flyableクラスのメソッド
duck.swim() # Swimmableクラスのメソッド
duck.quack() # Duckクラス独自のメソッド
多重継承は、複数の親クラスから同時に機能を継承できる便利な仕組みですが、注意が必要です。
理由は次の通りです。
- メソッドの衝突:複数の親クラスで同名のメソッドがあると、どのクラスのメソッドが呼ばれるか分かりづらくなります。
- 呼び出し順序の複雑さ:Pythonは「MRO(Method Resolution Order)」というルールでメソッド探索順を決めますが、構造が複雑になると予期しない動作を引き起こすことがあります。
- 可読性・保守性の低下:継承関係が増えるほど、コードの理解や変更が難しくなります。
そのため、多重継承は慎重に設計し、必要最小限に使うのが望ましいです。
抽象基底クラス
抽象基底クラスは、インスタンス化できないクラスで、他のクラスのテンプレートとして機能します。Pythonではabcモジュールを使用して実現します。
「abcモジュール」というのは正式名称ではなく、Python標準ライブラリに含まれるモジュール名 abc(=Abstract Base Classes の略)をそのまま通称として呼んでいるものです。
abcモジュールは、Pythonで**抽象基底クラス(Abstract Base Class)**を定義するためのモジュールです。共通のメソッドやインターフェースを強制することで、継承先のクラスが特定のメソッドを必ず実装するようにできます。@abstractmethodデコレータを使って抽象メソッドを定義します。
from abc import ABC, abstractmethod
class Employee(ABC):
def __init__(self, name, employee_id):
self.name = name
self.employee_id = employee_id
@abstractmethod
def calculate_salary(self):
pass
@abstractmethod
def display_role(self):
pass
def display_info(self):
print(f"名前: {self.name}")
print(f"社員ID: {self.employee_id}")
class FullTimeEmployee(Employee):
def __init__(self, name, employee_id, monthly_salary):
super().__init__(name, employee_id)
self.monthly_salary = monthly_salary
def calculate_salary(self):
return self.monthly_salary
def display_role(self):
print("正社員")
class PartTimeEmployee(Employee):
def __init__(self, name, employee_id, hourly_wage, hours_worked):
super().__init__(name, employee_id)
self.hourly_wage = hourly_wage
self.hours_worked = hours_worked
def calculate_salary(self):
return self.hourly_wage * self.hours_worked
def display_role(self):
print("パートタイム社員")
isinstance()とissubclass()
isinstance()とissubclass()関数は、継承関係を確認するために使用されます。isinstance() はオブジェクトが特定のクラス(またはそのサブクラス)のインスタンスかを判定し、issubclass() はクラス同士の継承関係を確認します。
ここでは my_car が Car と Vehicle のインスタンスであり、Car は Vehicle のサブクラスであることがわかります。
# 継承関係の確認
print(isinstance(my_car, Car)) # True
print(isinstance(my_car, Vehicle)) # True
print(issubclass(Car, Vehicle)) # True
print(issubclass(Vehicle, Car)) # False
継承の設計原則
継承を使用する際には、以下の原則を考慮することが重要です。
- Liskovの置換原則: 子クラスは親クラスと置換可能でなければならない
- 単一責任の原則: 各クラスは単一の責任のみを持つべき
- 依存性逆転の原則: 具象クラスではなく抽象クラスに依存する
実践的な例:ゲームのキャラクターシステム
継承とポリモーフィズムの実践的な使用例として、ゲームのキャラクターシステムを実装してみましょう。このコードはGameCharacterを基底クラスとして、Warrior・Mage・Archerがそれぞれ独自の攻撃方法を実装しています。
共通メソッド(attack, take_damage, heal, など)を共有しつつ、クラスごとに異なる挙動を示すことで、柔軟なバトルシステムを実現しています。battle()関数では、それぞれのキャラクターが交互に攻撃し、倒れるまで戦う流れを表しています。
class GameCharacter:
def __init__(self, name, level, health):
self.name = name
self.level = level
self.health = health
self.max_health = health
def attack(self):
raise NotImplementedError("サブクラスで実装してください")
def take_damage(self, damage):
self.health = max(0, self.health - damage)
print(f"{self.name}は{damage}のダメージを受けた!")
if self.health == 0:
print(f"{self.name}は倒れた!")
def heal(self, amount):
self.health = min(self.max_health, self.health + amount)
print(f"{self.name}は{amount}回復した!")
def is_alive(self):
return self.health > 0
def display_status(self):
print(f"{self.name} Lv{self.level} HP: {self.health}/{self.max_health}")
class Warrior(GameCharacter):
def __init__(self, name, level, health, strength):
super().__init__(name, level, health)
self.strength = strength
def attack(self):
damage = self.strength + self.level
print(f"{self.name}の剣の攻撃! {damage}のダメージ!")
return damage
def power_attack(self):
damage = self.strength * 2 + self.level
self.take_damage(5) # 反動ダメージ
print(f"{self.name}の強力な一撃! {damage}のダメージ!")
return damage
class Mage(GameCharacter):
def __init__(self, name, level, health, magic_power):
super().__init__(name, level, health)
self.magic_power = magic_power
self.mana = 100
def attack(self):
damage = self.magic_power // 2
print(f"{self.name}の魔法の攻撃! {damage}のダメージ!")
return damage
def fire_ball(self):
if self.mana >= 20:
damage = self.magic_power + self.level
self.mana -= 20
print(f"{self.name}のファイアボール! {damage}のダメージ!")
return damage
else:
print("マナが足りない!")
return 0
def display_status(self):
super().display_status()
print(f"MP: {self.mana}/100")
class Archer(GameCharacter):
def __init__(self, name, level, health, dexterity):
super().__init__(name, level, health)
self.dexterity = dexterity
self.arrows = 10
def attack(self):
if self.arrows > 0:
damage = self.dexterity + self.level // 2
self.arrows -= 1
print(f"{self.name}の弓の攻撃! {damage}のダメージ!")
return damage
else:
print("矢が足りない!")
return 0
def multi_shot(self):
if self.arrows >= 3:
damage = self.dexterity * 1.5
self.arrows -= 3
print(f"{self.name}のマルチショット! {damage}のダメージ!")
return damage
else:
print("矢が足りない!")
return 0
def display_status(self):
super().display_status()
print(f"矢: {self.arrows}本")
# ポリモーフィズムの実演
def battle(character1, character2):
print("=== バトル開始 ===")
while character1.is_alive() and character2.is_alive():
# キャラクター1の攻撃
damage = character1.attack()
character2.take_damage(damage)
if not character2.is_alive():
break
# キャラクター2の攻撃
damage = character2.attack()
character1.take_damage(damage)
if character1.is_alive():
print(f"{character1.name}の勝利!")
else:
print(f"{character2.name}の勝利!")
# 使用例
warrior = Warrior("勇者", 10, 100, 15)
mage = Mage("魔法使い", 8, 80, 25)
characters = [warrior, mage]
for character in characters:
character.display_status()
character.attack()
print()
# バトルの実行
battle(warrior, mage)
まとめ
継承とポリモーフィズムは、オブジェクト指向プログラミングの強力な概念です。継承によりコードの再利用性と保守性が向上し、ポリモーフィズムにより柔軟で拡張性の高い設計が可能になります。
適切に使用することで、以下の利点が得られます。
- コードの重複を減らす
- 階層的な関係を自然に表現する
- 新しい機能の追加が容易になる
- 同じインターフェースで異なる振る舞いを実現する
次回は「カプセル化とアクセス修飾子」について学び、オブジェクトの内部状態を保護する方法について探求していきます。
演習問題
初級問題
問題1
次のコードを完成させて、Animalクラスを継承したDogクラスとCatクラスを作成してください。各クラスはspeakメソッドを持ち、それぞれ異なる鳴き声を出力するようにしてください。
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
pass
問題2Vehicleクラスを継承したCarクラスを作成してください。Carクラスはdoors属性を追加し、display_infoメソッドをオーバーライドしてドア数も表示するようにしてください。
class Vehicle:
def __init__(self, brand, model):
self.brand = brand
self.model = model
def display_info(self):
print(f"{self.brand} {self.model}")
問題3
次のクラス階層で、RectangleクラスとCircleクラスにareaメソッドを実装してください。
class Shape:
def area(self):
pass
中級問題
問題4Employee抽象基底クラスを作成し、それを継承したManagerクラスとDeveloperクラスを実装してください。各クラスはcalculate_bonusメソッドを実装し、ボーナス計算方法が異なるようにしてください。
問題5BankAccountクラスを継承したSavingsAccountクラスとCheckingAccountクラスを作成してください。SavingsAccountは利息計算機能を、CheckingAccountは手数料計算機能を追加してください。
問題6
ゲームのアイテムシステムを設計してください。Item基本クラスと、それを継承したWeapon、Armor、Potionクラスを作成し、ポリモーフィズムを使用して各アイテムの使用効果を実装してください。
問題7Personクラスを継承したStudentクラスとTeacherクラスを作成してください。多重継承を使用して、Researcherクラスも継承するGraduateStudentクラスを作成してください。
問題8
動物の階層構造を作成してください。Mammal、Bird、Reptileクラスを作成し、それぞれ適切なサブクラスを定義してください。各クラスはmoveメソッドを持ち、移動方法が異なるようにしてください。
問題9
電子機器のクラス階層を作成してください。ElectronicDevice基本クラスからSmartPhone、Laptop、Tabletクラスを派生させ、各デバイス特有の機能をメソッドとして追加してください。
上級問題
問題10
図形描画システムを設計してください。Drawable抽象クラスを定義し、Point、Line、Rectangle、Circleクラスで実装してください。ポリモーフィズムを使用してすべての図形を同じインターフェースで描画できるようにしてください。
問題11
在庫管理システムを拡張してください。Product基本クラスからPhysicalProductとDigitalProductクラスを派生させ、それぞれに適した在庫管理方法を実装してください。ポリモーフィズムを使用して在庫確認を統一的なインターフェースで行えるようにしてください。
問題12
通知システムを設計してください。Notification抽象クラスを定義し、EmailNotification、SMSNotification、PushNotificationクラスで実装してください。すべての通知タイプを同じインターフェースで送信できるようにし、新しい通知タイプの追加が容易な設計にしてください。
初級問題
問題1 解答
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
pass
class Dog(Animal):
def speak(self):
print(f"{self.name}はワンワン吠えます")
class Cat(Animal):
def speak(self):
print(f"{self.name}はニャーと鳴きます")
# 使用例
dog = Dog("ポチ")
cat = Cat("タマ")
dog.speak() # ポチはワンワン吠えます
cat.speak() # タマはニャーと鳴きます
解説: 親クラスのメソッドをオーバーライドして、各子クラスで異なる振る舞いを実装しています。
問題2 解答
class Vehicle:
def __init__(self, brand, model):
self.brand = brand
self.model = model
def display_info(self):
print(f"{self.brand} {self.model}")
class Car(Vehicle):
def __init__(self, brand, model, doors):
super().__init__(brand, model)
self.doors = doors
def display_info(self):
print(f"{self.brand} {self.model} - {self.doors}ドア")
# 使用例
vehicle = Vehicle("Toyota", "Camry")
car = Car("Honda", "Civic", 4)
vehicle.display_info() # Toyota Camry
car.display_info() # Honda Civic - 4ドア
解説: super()で親クラスのコンストラクタを呼び出し、メソッドをオーバーライドして追加情報を表示しています。
問題3 解答
class Shape:
def area(self):
pass
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14159 * self.radius ** 2
# 使用例
shapes = [Rectangle(5, 3), Circle(2)]
for shape in shapes:
print(f"面積: {shape.area():.2f}")
# 出力:
# 面積: 15.00
# 面積: 12.57
解説: ポリモーフィズムを活用して、異なる図形オブジェクトを同じインターフェースで扱っています。
中級問題
問題4 解答
from abc import ABC, abstractmethod
class Employee(ABC):
def __init__(self, name, salary):
self.name = name
self.salary = salary
@abstractmethod
def calculate_bonus(self):
pass
def display_info(self):
print(f"{self.name} - 基本給: {self.salary}円")
class Manager(Employee):
def calculate_bonus(self):
return self.salary * 0.3 # 基本給の30%
def display_info(self):
super().display_info()
print(f"ボーナス: {self.calculate_bonus()}円")
class Developer(Employee):
def __init__(self, name, salary, projects_completed):
super().__init__(name, salary)
self.projects_completed = projects_completed
def calculate_bonus(self):
return self.salary * 0.2 + self.projects_completed * 5000
def display_info(self):
super().display_info()
print(f"完了プロジェクト: {self.projects_completed}")
print(f"ボーナス: {self.calculate_bonus()}円")
# 使用例
employees = [
Manager("山田部長", 800000),
Developer("佐藤エンジニア", 600000, 5)
]
for employee in employees:
employee.display_info()
print()
問題5 解答
class BankAccount:
def __init__(self, account_number, balance=0):
self.account_number = account_number
self.balance = balance
def deposit(self, amount):
if amount > 0:
self.balance += amount
print(f"{amount}円預け入れました")
return self.balance
def withdraw(self, amount):
if 0 < amount <= self.balance:
self.balance -= amount
print(f"{amount}円引き出しました")
return self.balance
def display_balance(self):
print(f"口座番号: {self.account_number}")
print(f"残高: {self.balance}円")
class SavingsAccount(BankAccount):
def __init__(self, account_number, balance=0, interest_rate=0.02):
super().__init__(account_number, balance)
self.interest_rate = interest_rate
def calculate_interest(self, months=1):
interest = self.balance * self.interest_rate * months / 12
print(f"{months}ヶ月分の利息: {interest:.2f}円")
return interest
def apply_interest(self, months=1):
interest = self.calculate_interest(months)
self.balance += interest
print(f"利息を適用しました")
class CheckingAccount(BankAccount):
def __init__(self, account_number, balance=0, transaction_fee=100):
super().__init__(account_number, balance)
self.transaction_fee = transaction_fee
self.transaction_count = 0
def withdraw(self, amount):
result = super().withdraw(amount)
self.transaction_count += 1
if self.transaction_count > 3:
self.balance -= self.transaction_fee
print(f"取引手数料: {self.transaction_fee}円")
return result
def reset_transaction_count(self):
self.transaction_count = 0
print("取引回数をリセットしました")
# 使用例
savings = SavingsAccount("SAV001", 100000, 0.03)
checking = CheckingAccount("CHK001", 50000, 150)
print("=== 普通預金口座 ===")
savings.deposit(50000)
savings.calculate_interest(6)
savings.apply_interest()
savings.display_balance()
print("\n=== 当座預金口座 ===")
for _ in range(5):
checking.withdraw(1000)
checking.display_balance()
問題6 解答
from abc import ABC, abstractmethod
class Item(ABC):
def __init__(self, name, value):
self.name = name
self.value = value
@abstractmethod
def use(self, character):
pass
def __str__(self):
return f"{self.name} (価値: {self.value})"
class Weapon(Item):
def __init__(self, name, value, attack_power):
super().__init__(name, value)
self.attack_power = attack_power
def use(self, character):
print(f"{self.name}で攻撃! {self.attack_power}のダメージ!")
return self.attack_power
class Armor(Item):
def __init__(self, name, value, defense_power):
super().__init__(name, value)
self.defense_power = defense_power
def use(self, character):
print(f"{self.name}を装備! 防御力+{self.defense_power}")
return self.defense_power
class Potion(Item):
def __init__(self, name, value, heal_amount):
super().__init__(name, value)
self.heal_amount = heal_amount
def use(self, character):
print(f"{self.name}を使用! HPを{self.heal_amount}回復!")
return self.heal_amount
class GameCharacter:
def __init__(self, name, health=100):
self.name = name
self.health = health
self.inventory = []
def use_item(self, item_index):
if 0 <= item_index < len(self.inventory):
item = self.inventory[item_index]
result = item.use(self)
if isinstance(item, Potion):
self.health += result
return result
return 0
def add_item(self, item):
self.inventory.append(item)
print(f"{item.name}を入手しました")
# 使用例
character = GameCharacter("勇者")
# アイテムの追加
character.add_item(Weapon("鋼の剣", 100, 15))
character.add_item(Armor("鉄の鎧", 150, 10))
character.add_item(Potion("回復薬", 50, 30))
# ポリモーフィズムの実演
print("=== アイテム使用 ===")
for i in range(len(character.inventory)):
character.use_item(i)
print()
問題7 解答
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def introduce(self):
print(f"私は{self.name}です。{self.age}歳です。")
class Student(Person):
def __init__(self, name, age, student_id):
super().__init__(name, age)
self.student_id = student_id
self.courses = []
def enroll_course(self, course):
self.courses.append(course)
print(f"{course}を履修登録しました")
def introduce(self):
super().introduce()
print(f"学籍番号: {self.student_id}")
class Teacher(Person):
def __init__(self, name, age, subject):
super().__init__(name, age)
self.subject = subject
def teach(self):
print(f"{self.subject}を教えています")
def introduce(self):
super().introduce()
print(f"担当科目: {self.subject}")
class Researcher:
def __init__(self, research_field):
self.research_field = research_field
self.publications = []
def add_publication(self, publication):
self.publications.append(publication)
print(f"論文 '{publication}' を追加しました")
def conduct_research(self):
print(f"{self.research_field}の研究を行っています")
class GraduateStudent(Student, Researcher):
def __init__(self, name, age, student_id, research_field):
Student.__init__(self, name, age, student_id)
Researcher.__init__(self, research_field)
def introduce(self):
super().introduce()
print(f"研究分野: {self.research_field}")
print(f"論文数: {len(self.publications)}")
# 使用例
grad_student = GraduateStudent("鈴木花子", 25, "G12345", "人工知能")
grad_student.introduce()
grad_student.enroll_course("機械学習")
grad_student.add_publication("深層学習の応用")
grad_student.conduct_research()
問題8 解答
from abc import ABC, abstractmethod
class Animal(ABC):
def __init__(self, name, habitat):
self.name = name
self.habitat = habitat
@abstractmethod
def move(self):
pass
def display_info(self):
print(f"{self.name} - 生息地: {self.habitat}")
class Mammal(Animal):
def __init__(self, name, habitat, fur_color):
super().__init__(name, habitat)
self.fur_color = fur_color
def move(self):
print(f"{self.name}は走ります")
def display_info(self):
super().display_info()
print(f"毛色: {self.fur_color}")
class Bird(Animal):
def __init__(self, name, habitat, wingspan):
super().__init__(name, habitat)
self.wingspan = wingspan
def move(self):
print(f"{self.name}は飛びます")
def display_info(self):
super().display_info()
print(f"翼幅: {self.wingspan}cm")
class Reptile(Animal):
def __init__(self, name, habitat, scale_type):
super().__init__(name, habitat)
self.scale_type = scale_type
def move(self):
print(f"{self.name}は這います")
def display_info(self):
super().display_info()
print(f"鱗の種類: {self.scale_type}")
# 哺乳類のサブクラス
class Lion(Mammal):
def __init__(self, name, habitat, fur_color, mane_size):
super().__init__(name, habitat, fur_color)
self.mane_size = mane_size
def move(self):
print(f"{self.name}は悠然と歩きます")
def roar(self):
print("ガオー!")
# 鳥類のサブクラス
class Eagle(Bird):
def __init__(self, name, habitat, wingspan, vision_range):
super().__init__(name, habitat, wingspan)
self.vision_range = vision_range
def move(self):
print(f"{self.name}は高く舞い上がります")
def hunt(self):
print("獲物を探しています")
# 爬虫類のサブクラス
class Snake(Reptile):
def __init__(self, name, habitat, scale_type, length):
super().__init__(name, habitat, scale_type)
self.length = length
def move(self):
print(f"{self.name}は静かに這います")
def shed_skin(self):
print("脱皮しました")
# 使用例
animals = [
Lion("シンバ", "サバンナ", "金色", "大きい"),
Eagle("ホーク", "山岳地帯", 200, "2km"),
Snake("ナーガ", "ジャングル", "光沢のある", 3.0)
]
for animal in animals:
animal.display_info()
animal.move()
print()
問題9 解答
class ElectronicDevice:
def __init__(self, brand, model, power_consumption):
self.brand = brand
self.model = model
self.power_consumption = power_consumption
self.is_on = False
def turn_on(self):
self.is_on = True
print(f"{self.brand} {self.model}の電源を入れました")
def turn_off(self):
self.is_on = False
print(f"{self.brand} {self.model}の電源を切りました")
def get_power_usage(self, hours):
return self.power_consumption * hours
def display_info(self):
status = "ON" if self.is_on else "OFF"
print(f"{self.brand} {self.model} - 状態: {status}")
class SmartPhone(ElectronicDevice):
def __init__(self, brand, model, power_consumption, os, screen_size):
super().__init__(brand, model, power_consumption)
self.os = os
self.screen_size = screen_size
self.apps = []
def make_call(self, number):
if self.is_on:
print(f"{number}に電話をかけます")
else:
print("電源が入っていません")
def install_app(self, app_name):
self.apps.append(app_name)
print(f"アプリ '{app_name}' をインストールしました")
def display_info(self):
super().display_info()
print(f"OS: {self.os}, 画面サイズ: {self.screen_size}インチ")
print(f"インストールアプリ数: {len(self.apps)}")
class Laptop(ElectronicDevice):
def __init__(self, brand, model, power_consumption, processor, ram):
super().__init__(brand, model, power_consumption)
self.processor = processor
self.ram = ram
self.battery_level = 100
def code(self):
if self.is_on:
print("コーディング中...")
self.battery_level -= 5
else:
print("電源が入っていません")
def check_battery(self):
print(f"バッテリー残量: {self.battery_level}%")
def display_info(self):
super().display_info()
print(f"プロセッサ: {self.processor}, RAM: {self.ram}GB")
class Tablet(ElectronicDevice):
def __init__(self, brand, model, power_consumption, stylus_support, storage):
super().__init__(brand, model, power_consumption)
self.stylus_support = stylus_support
self.storage = storage
self.drawing_apps = []
def draw(self):
if self.is_on and self.stylus_support:
print("絵を描いています")
elif not self.stylus_support:
print("このタブレットはスタイラス非対応です")
else:
print("電源が入っていません")
def install_drawing_app(self, app_name):
self.drawing_apps.append(app_name)
print(f"描画アプリ '{app_name}' をインストールしました")
def display_info(self):
super().display_info()
support = "対応" if self.stylus_support else "非対応"
print(f"スタイラス: {support}, ストレージ: {self.storage}GB")
# 使用例
devices = [
SmartPhone("Apple", "iPhone 15", 5, "iOS", 6.1),
Laptop("Dell", "XPS 13", 45, "Intel i7", 16),
Tablet("Samsung", "Galaxy Tab", 8, True, 128)
]
for device in devices:
device.turn_on()
device.display_info()
# デバイス特有の機能
if isinstance(device, SmartPhone):
device.make_call("090-1234-5678")
device.install_app("LINE")
elif isinstance(device, Laptop):
device.code()
device.check_battery()
elif isinstance(device, Tablet):
device.draw()
device.install_drawing_app("Procreate")
print()
上級問題
問題10 解答
from abc import ABC, abstractmethod
import math
class Drawable(ABC):
@abstractmethod
def draw(self):
pass
@abstractmethod
def get_area(self):
pass
@abstractmethod
def get_perimeter(self):
pass
class Point(Drawable):
def __init__(self, x, y):
self.x = x
self.y = y
def draw(self):
print(f"点を描画: ({self.x}, {self.y})")
def get_area(self):
return 0
def get_perimeter(self):
return 0
def distance_to(self, other_point):
return math.sqrt((self.x - other_point.x)**2 + (self.y - other_point.y)**2)
class Line(Drawable):
def __init__(self, start_point, end_point):
self.start_point = start_point
self.end_point = end_point
def draw(self):
print(f"線を描画: ({self.start_point.x}, {self.start_point.y}) -> ({self.end_point.x}, {self.end_point.y})")
def get_area(self):
return 0
def get_perimeter(self):
return self.start_point.distance_to(self.end_point)
def get_length(self):
return self.get_perimeter()
class Rectangle(Drawable):
def __init__(self, x, y, width, height):
self.x = x
self.y = y
self.width = width
self.height = height
def draw(self):
print(f"長方形を描画: 位置({self.x}, {self.y}), サイズ({self.width}×{self.height})")
def get_area(self):
return self.width * self.height
def get_perimeter(self):
return 2 * (self.width + self.height)
class Circle(Drawable):
def __init__(self, x, y, radius):
self.x = x
self.y = y
self.radius = radius
def draw(self):
print(f"円を描画: 中心({self.x}, {self.y}), 半径{self.radius}")
def get_area(self):
return math.pi * self.radius ** 2
def get_perimeter(self):
return 2 * math.pi * self.radius
class Drawing:
def __init__(self):
self.shapes = []
def add_shape(self, shape):
self.shapes.append(shape)
print("図形を追加しました")
def draw_all(self):
print("=== すべての図形を描画 ===")
for shape in self.shapes:
shape.draw()
print(f" 面積: {shape.get_area():.2f}, 周囲長: {shape.get_perimeter():.2f}")
print("========================")
def total_area(self):
return sum(shape.get_area() for shape in self.shapes)
def total_perimeter(self):
return sum(shape.get_perimeter() for shape in self.shapes)
# 使用例
drawing = Drawing()
# 様々な図形を追加
drawing.add_shape(Point(0, 0))
drawing.add_shape(Line(Point(0, 0), Point(3, 4)))
drawing.add_shape(Rectangle(1, 1, 5, 3))
drawing.add_shape(Circle(2, 2, 3))
# ポリモーフィズムで一括描画
drawing.draw_all()
print(f"総面積: {drawing.total_area():.2f}")
print(f"総周囲長: {drawing.total_perimeter():.2f}")
問題11 解答
from abc import ABC, abstractmethod
from datetime import datetime
class Product(ABC):
def __init__(self, product_id, name, price):
self.product_id = product_id
self.name = name
self.price = price
self.created_at = datetime.now()
@abstractmethod
def check_inventory(self):
pass
@abstractmethod
def update_inventory(self, quantity):
pass
@abstractmethod
def can_sell(self, quantity):
pass
def display_info(self):
print(f"ID: {self.product_id}")
print(f"商品名: {self.name}")
print(f"価格: {self.price}円")
class PhysicalProduct(Product):
def __init__(self, product_id, name, price, stock_quantity, weight):
super().__init__(product_id, name, price)
self.stock_quantity = stock_quantity
self.weight = weight
def check_inventory(self):
return self.stock_quantity
def update_inventory(self, quantity):
if self.stock_quantity + quantity >= 0:
self.stock_quantity += quantity
return True
return False
def can_sell(self, quantity):
return self.stock_quantity >= quantity
def calculate_shipping_cost(self, distance):
return self.weight * distance * 0.1
def display_info(self):
super().display_info()
print(f"在庫数: {self.stock_quantity}")
print(f"重量: {self.weight}kg")
class DigitalProduct(Product):
def __init__(self, product_id, name, price, file_size, download_limit=None):
super().__init__(product_id, name, price)
self.file_size = file_size
self.download_limit = download_limit
self.download_count = 0
def check_inventory(self):
if self.download_limit is None:
return "無制限"
return f"{self.download_limit - self.download_count}回"
def update_inventory(self, quantity):
if self.download_limit is None:
return True
if self.download_count + quantity <= self.download_limit:
self.download_count += quantity
return True
return False
def can_sell(self, quantity):
if self.download_limit is None:
return True
return self.download_count + quantity <= self.download_limit
def get_file_info(self):
return f"ファイルサイズ: {self.file_size}MB, ダウンロード回数: {self.download_count}"
def display_info(self):
super().display_info()
print(f"ファイルサイズ: {self.file_size}MB")
if self.download_limit:
print(f"ダウンロード制限: {self.download_limit}回")
print(f"現在のダウンロード回数: {self.download_count}")
class InventoryManager:
def __init__(self):
self.products = []
def add_product(self, product):
self.products.append(product)
print(f"商品 '{product.name}' を追加しました")
def sell_product(self, product_id, quantity):
for product in self.products:
if product.product_id == product_id and product.can_sell(quantity):
product.update_inventory(quantity)
total_price = product.price * quantity
print(f"商品 '{product.name}' {quantity}個を販売しました")
print(f"売上: {total_price}円")
# 物理商品の場合、配送コストを計算
if isinstance(product, PhysicalProduct):
shipping_cost = product.calculate_shipping_cost(100) # 仮の距離
print(f"配送コスト: {shipping_cost}円")
return True
print("販売できませんでした")
return False
def display_inventory_report(self):
print("\n=== 在庫レポート ===")
for product in self.products:
product.display_info()
print(f"在庫状態: {product.check_inventory()}")
print("---")
print("===================")
# 使用例
inventory_manager = InventoryManager()
# 商品の追加
physical_product = PhysicalProduct("P001", "ノートパソコン", 150000, 10, 2.5)
digital_product = DigitalProduct("D001", "電子書籍", 2000, 50, 3)
inventory_manager.add_product(physical_product)
inventory_manager.add_product(digital_product)
# 在庫レポート表示
inventory_manager.display_inventory_report()
# 商品販売
print("\n=== 販売処理 ===")
inventory_manager.sell_product("P001", 2)
inventory_manager.sell_product("D001", 1)
# 最終レポート
inventory_manager.display_inventory_report()
問題12 解答
from abc import ABC, abstractmethod
from datetime import datetime
class Notification(ABC):
def __init__(self, recipient, message):
self.recipient = recipient
self.message = message
self.sent_at = None
self.status = "準備中"
@abstractmethod
def send(self):
pass
@abstractmethod
def get_notification_type(self):
pass
def mark_sent(self):
self.sent_at = datetime.now()
self.status = "送信済み"
def get_status(self):
return f"{self.get_notification_type()} - {self.status}"
def display_info(self):
print(f"通知タイプ: {self.get_notification_type()}")
print(f"受信者: {self.recipient}")
print(f"メッセージ: {self.message}")
print(f"ステータス: {self.status}")
if self.sent_at:
print(f"送信日時: {self.sent_at.strftime('%Y-%m-%d %H:%M:%S')}")
class EmailNotification(Notification):
def __init__(self, recipient, message, subject, from_address):
super().__init__(recipient, message)
self.subject = subject
self.from_address = from_address
def send(self):
print(f"メール送信: {self.from_address} -> {self.recipient}")
print(f"件名: {self.subject}")
print(f"本文: {self.message}")
self.mark_sent()
return True
def get_notification_type(self):
return "メール通知"
def display_info(self):
super().display_info()
print(f"差出人: {self.from_address}")
print(f"件名: {self.subject}")
class SMSNotification(Notification):
def __init__(self, recipient, message, phone_number):
super().__init__(recipient, message)
self.phone_number = phone_number
def send(self):
print(f"SMS送信: {self.phone_number}")
print(f"メッセージ: {self.message}")
self.mark_sent()
return True
def get_notification_type(self):
return "SMS通知"
def display_info(self):
super().display_info()
print(f"電話番号: {self.phone_number}")
class PushNotification(Notification):
def __init__(self, recipient, message, device_token, app_name):
super().__init__(recipient, message)
self.device_token = device_token
self.app_name = app_name
def send(self):
print(f"プッシュ通知送信: {self.app_name}")
print(f"デバイス: {self.device_token}")
print(f"メッセージ: {self.message}")
self.mark_sent()
return True
def get_notification_type(self):
return "プッシュ通知"
def display_info(self):
super().display_info()
print(f"アプリ名: {self.app_name}")
print(f"デバイストークン: {self.device_token}")
class NotificationManager:
def __init__(self):
self.notifications = []
self.sent_count = 0
def add_notification(self, notification):
self.notifications.append(notification)
print(f"通知を追加しました: {notification.get_notification_type()}")
def send_all(self):
print("\n=== 一括通知送信 ===")
success_count = 0
for notification in self.notifications:
if notification.status != "送信済み":
if notification.send():
success_count += 1
print("---")
self.sent_count += success_count
print(f"{success_count}件の通知を送信しました")
def send_by_type(self, notification_type):
print(f"\n=== {notification_type}のみ送信 ===")
count = 0
for notification in self.notifications:
if (notification.get_notification_type() == notification_type and
notification.status != "送信済み"):
if notification.send():
count += 1
print("---")
print(f"{count}件の{notification_type}を送信しました")
def display_statistics(self):
print("\n=== 通知統計 ===")
type_count = {}
for notification in self.notifications:
n_type = notification.get_notification_type()
type_count[n_type] = type_count.get(n_type, 0) + 1
for n_type, count in type_count.items():
print(f"{n_type}: {count}件")
print(f"総送信済み数: {self.sent_count}件")
print("================")
def display_all_notifications(self):
print("\n=== すべての通知 ===")
for i, notification in enumerate(self.notifications, 1):
print(f"{i}. ", end="")
notification.display_info()
print()
# 使用例
notification_manager = NotificationManager()
# 様々なタイプの通知を作成
email_notif = EmailNotification(
"user@example.com",
"お知らせがあります",
"重要なお知らせ",
"noreply@company.com"
)
sms_notif = SMSNotification(
"山田太郎",
"本日15時にお伺いします",
"090-1234-5678"
)
push_notif = PushNotification(
"user123",
"新しいメッセージが届きました",
"device_token_abc123",
"MyApp"
)
# 通知をマネージャーに追加
notification_manager.add_notification(email_notif)
notification_manager.add_notification(sms_notif)
notification_manager.add_notification(push_notif)
# 通知の表示
notification_manager.display_all_notifications()
# 統計表示
notification_manager.display_statistics()
# 通知送信
notification_manager.send_all()
# 最終統計
notification_manager.display_statistics()