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}")
実行結果:
Toyota Camry
Honda Civic - 4ドア
問題3
次のクラス階層で、RectangleクラスとCircleクラスにareaメソッドを実装してください。
class Shape:
def area(self):
pass
実行結果:
面積: 15.00
面積: 12.57
中級問題
問題4
Employeeクラスを親クラスとして作成し、ManagerクラスとDeveloperクラスを継承して作成してください。Managerクラスのボーナスは基本給の30%とします。Developerクラスのボーナスは基本給の20%に加えて、完了プロジェクト数 × 5000円とします。以下の情報を表示してください。
- 名前
- 基本給
- ボーナス
- 完了プロジェクト数(Developerのみ)
使用データ
- Manager(“山田部長”, 800000)
- Developer(“佐藤エンジニア”, 600000, 5)
まずEmployeeクラスを作成します。ここでは社員共通の「名前」と「基本給」を保存できるようにします。その後、display_infoメソッドを作成して社員情報を表示できるようにします。
次にManagerクラスを作成します。Employeeクラスを継承し、calculate_bonusメソッドで「基本給 × 0.3」を計算します。display_infoメソッドでは親クラスのdisplay_infoを呼び出したあとにボーナスを表示します。
その後、Developerクラスを作成します。Developerクラスでは追加で「完了プロジェクト数」を保存します。calculate_bonusメソッドでは「基本給 × 0.2 + プロジェクト数 × 5000」を計算します。最後にManagerオブジェクトとDeveloperオブジェクトを作成し、for文で順番にdisplay_infoメソッドを実行して結果を表示します。
実行結果:
山田部長 - 基本給: 800000円
ボーナス: 240000.0円
佐藤エンジニア - 基本給: 600000円
完了プロジェクト: 5
ボーナス: 145000.0円
問題5
BankAccountクラスを親クラスとして作成し、SavingsAccountクラスとCheckingAccountクラスを継承して作成してください。SavingsAccountクラスでは利息計算を行います。利率は0.03とし、6ヶ月分の利息を計算してください。CheckingAccountクラスでは、4回目以降の引き出し時に取引手数料150円を差し引いてください。以下の操作を実行してください。
- 普通預金口座へ50000円を預け入れる
- 6ヶ月分の利息を計算する
- 利息を残高へ追加する
- 当座預金口座から1000円を5回引き出す
- 最後に口座情報と残高を表示する
使用データ
- SavingsAccount(“SAV001”, 100000, 0.03)
- CheckingAccount(“CHK001”, 50000, 150)
まずBankAccountクラスを作成します。ここでは「口座番号」と「残高」を保存します。その後、depositメソッドで入金処理、withdrawメソッドで出金処理、display_balanceメソッドで口座情報を表示できるようにします。
次にSavingsAccountクラスを作成します。BankAccountクラスを継承し、追加で「利率」を保存します。calculate_interestメソッドでは「残高 × 利率 × 月数 ÷ 12」で利息を計算します。apply_interestメソッドでは計算した利息を残高へ追加します。
その後、CheckingAccountクラスを作成します。ここでは「取引手数料」と「取引回数」を追加します。withdrawメソッドを上書きし、4回目以降の出金時には手数料150円を残高から引きます。最後にSavingsAccountオブジェクトとCheckingAccountオブジェクトを作成し、問題文の順番どおりにメソッドを実行して結果を表示します。
実行結果:
残高: 150375.0円
=== 当座預金口座 ===
1000円引き出しました
1000円引き出しました
1000円引き出しました
1000円引き出しました
取引手数料: 150円
1000円引き出しました
取引手数料: 150円
口座番号: CHK001
残高: 44700円
問題6
Itemクラスを親クラスとして作成し、Weaponクラス、Armorクラス、Potionクラスを継承して作成してください。Weaponクラスでは攻撃力を持つ武器を作成します。Armorクラスでは防御力を持つ防具を作成します。PotionクラスではHPを回復する回復アイテムを作成します。また、GameCharacterクラスを作成し、アイテムを持てるようにしてください。以下の処理を実行してください。
- 武器「鋼の剣」を追加する
- 防具「鉄の鎧」を追加する
- 回復アイテム「回復薬」を追加する
- 追加したアイテムを順番に使用する
使用データ
- Weapon(“鋼の剣”, 100, 15)
- Armor(“鉄の鎧”, 150, 10)
- Potion(“回復薬”, 50, 30)
表示内容
- アイテム名
- 攻撃力または防御力
- 回復量
- アイテム使用メッセージ
まずItemクラスを作成します。ここでは「アイテム名」と「価値」を保存できるようにします。また、useメソッドを用意し、子クラスで処理を作れるようにします。次にWeaponクラスを作成します。Itemクラスを継承し、「攻撃力」を追加します。useメソッドでは「○○で攻撃!」と表示し、攻撃力を返します。
その後、Armorクラスを作成します。ここでは「防御力」を追加し、useメソッドで「防御力+○○」を表示します。続いてPotionクラスを作成します。「回復量」を追加し、useメソッドで「HPを○○回復!」と表示します。
次にGameCharacterクラスを作成します。ここではキャラクター名、HP、持ち物リストを保存します。add_itemメソッドでアイテムを追加し、use_itemメソッドでアイテムを使用できるようにします。最後にGameCharacterオブジェクトを作成し、武器、防具、回復薬を追加します。その後、for文で順番にアイテムを使用し、結果を表示します。
実行結果:
鋼の剣を入手しました
鉄の鎧を入手しました
回復薬を入手しました
=== アイテム使用 ===
鋼の剣で攻撃! 15のダメージ!
鉄の鎧を装備! 防御力+10
回復薬を使用! HPを30回復!
問題7
Personクラスを親クラスとして作成し、StudentクラスとTeacherクラスを継承して作成してください。また、Researcherクラスを作成し、GraduateStudentクラスではStudentクラスとResearcherクラスの両方を継承してください。GraduateStudentクラスでは以下の情報を扱えるようにしてください。
- 名前
- 年齢
- 学籍番号
- 研究分野
- 履修科目
- 論文一覧
以下の処理を実行してください。
- 大学院生を作成する
- 自己紹介を表示する
- 「機械学習」を履修登録する
- 「深層学習の応用」という論文を追加する
- 研究内容を表示する
使用データ
- GraduateStudent(“鈴木花子”, 25, “G12345”, “人工知能”)
表示内容
- 名前
- 年齢
- 学籍番号
- 研究分野
- 論文数
- 履修登録メッセージ
- 研究メッセージ
まずPersonクラスを作成します。ここでは「名前」と「年齢」を保存します。introduceメソッドでは自己紹介を表示できるようにします。次にStudentクラスを作成します。Personクラスを継承し、「学籍番号」と「履修科目リスト」を追加します。enroll_courseメソッドでは履修科目を追加できるようにします。
その後、Teacherクラスを作成します。ここでは担当科目を保存し、teachメソッドで授業内容を表示します。続いてResearcherクラスを作成します。ここでは「研究分野」と「論文リスト」を保存します。add_publicationメソッドでは論文を追加し、conduct_researchメソッドでは研究内容を表示します。
次にGraduateStudentクラスを作成します。StudentクラスとResearcherクラスの両方を継承し、多重継承を利用します。コンストラクタでは両方の親クラスの初期化を行います。最後にGraduateStudentオブジェクトを作成し、自己紹介、履修登録、論文追加、研究内容表示を順番に実行します。
実行結果:
私は鈴木花子です。25歳です。
学籍番号: G12345
研究分野: 人工知能
論文数: 0
機械学習を履修登録しました
論文 '深層学習の応用' を追加しました
人工知能の研究を行っています
問題8
Animalクラスを抽象クラスとして作成し、以下のクラスを継承して作成してください。
- Mammalクラス(哺乳類)
- Birdクラス(鳥類)
- Reptileクラス(爬虫類)
さらに、それぞれのクラスを継承して以下のクラスを作成してください。
- Lionクラス
- Eagleクラス
- Snakeクラス
以下の動物データを使用してください。
- Lion(“シンバ”, “サバンナ”, “金色”, “大きい”)
- Eagle(“ホーク”, “山岳地帯”, 200, “2km”)
- Snake(“ナーガ”, “ジャングル”, “光沢のある”, 3.0)
まずAnimalクラスを作成します。ここでは「名前」と「生息地」を保存します。moveメソッドは抽象メソッドとして定義します。display_infoメソッドでは基本情報を表示します。
次にMammalクラス、Birdクラス、Reptileクラスを作成します。これらはAnimalクラスを継承します。それぞれに独自の属性を追加します。Mammalでは毛色、Birdでは翼幅、Reptileでは鱗の種類を保存します。その後、moveメソッドを各クラスで実装します。哺乳類は「走ります」、鳥類は「飛びます」、爬虫類は「這います」と表示するようにします。
続いてLionクラス、Eagleクラス、Snakeクラスを作成します。これらはさらに親クラスを継承します。それぞれ追加の属性を持たせます。Lionではたてがみの大きさ、Eagleでは視界範囲、Snakeでは体長を保存します。
各サブクラスではmoveメソッドをオーバーライドして、より具体的な移動方法を表示します。最後に3つの動物オブジェクトを作成し、animalsリストに格納します。for文で取り出し、display_infoメソッドとmoveメソッドを順番に実行します。
実行結果:
シンバ - 生息地: サバンナ
毛色: 金色
シンバは悠然と歩きます
ホーク - 生息地: 山岳地帯
翼幅: 200cm
ホークは高く舞い上がります
ナーガ - 生息地: ジャングル
鱗の種類: 光沢のある
ナーガは静かに這います
問題9
ElectronicDeviceクラスを親クラスとして作成し、以下のクラスを継承して作成してください。
- SmartPhoneクラス
- Laptopクラス
- Tabletクラス
SmartPhoneクラスでは以下の機能を実装してください。
- 電話をかける
- アプリをインストールする
Laptopクラスでは以下の機能を実装してください。
- コーディングを実行する
- バッテリー残量を表示する
Tabletクラスでは以下の機能を実装してください。
- 絵を描く
- 描画アプリをインストールする
以下のデータを使用してください。
- SmartPhone(“Apple”, “iPhone 15”, 5, “iOS”, 6.1)
- Laptop(“Dell”, “XPS 13”, 45, “Intel i7”, 16)
- Tablet(“Samsung”, “Galaxy Tab”, 8, True, 128)
各デバイスでは以下を実行してください。
- 電源を入れる
- デバイス情報を表示する
- デバイス専用機能を実行する
まずElectronicDeviceクラスを作成します。ここではブランド名、モデル名、消費電力を保存します。また、電源状態を管理するためにis_onを用意します。次にturn_onメソッドとturn_offメソッドを作成します。ここでは電源状態を変更し、メッセージを表示します。
その後、display_infoメソッドを作成します。ここではデバイス名と電源状態を表示します。続いてSmartPhoneクラスを作成します。ElectronicDeviceクラスを継承し、OS名と画面サイズを追加します。電話機能とアプリインストール機能も作成します。
次にLaptopクラスを作成します。プロセッサ名、RAM容量、バッテリー残量を追加します。codeメソッドではコーディング処理を行い、バッテリーを減少させます。その後、Tabletクラスを作成します。スタイラス対応情報とストレージ容量を追加します。drawメソッドでは絵を描く処理を実装します。
各クラスではdisplay_infoメソッドをオーバーライドし、親クラスの情報に加えて独自情報も表示できるようにします。最後に3つのデバイスオブジェクトを作成し、devicesリストへ格納します。for文で取り出し、電源を入れてから情報表示と専用機能を実行します。
実行結果:
Dell XPS 13の電源を入れました
Dell XPS 13 - 状態: ON
プロセッサ: Intel i7, RAM: 16GB
コーディング中...
バッテリー残量: 95%
Samsung Galaxy Tabの電源を入れました
Samsung Galaxy Tab - 状態: ON
スタイラス: 対応, ストレージ: 128GB
絵を描いています
描画アプリ 'Procreate' をインストールしました
上級問題
問題10
Drawableクラスを抽象クラスとして作成し、以下のメソッドを定義してください。
- drawメソッド
- get_areaメソッド
- get_perimeterメソッド
その後、Drawableクラスを継承して以下のクラスを作成してください。
- Pointクラス
- Lineクラス
- Rectangleクラス
- Circleクラス
各クラスでは図形情報を保持し、drawメソッドで図形情報を表示してください。
また、面積と周囲長を計算してください。
以下のデータを使用してください。
- Point(0, 0)
- Line(Point(0, 0), Point(3, 4))
- Rectangle(1, 1, 5, 3)
- Circle(2, 2, 3)
Drawingクラスを作成し、複数の図形を管理してください。
最後に以下の処理を実行してください。
- 図形を追加する
- すべての図形を描画する
- 総面積を表示する
- 総周囲長を表示する
実行結果:
=== すべての図形を描画 ===
点を描画: (0, 0)
面積: 0.00, 周囲長: 0.00
線を描画: (0, 0) -> (3, 4)
面積: 0.00, 周囲長: 5.00
長方形を描画: 位置(1, 1), サイズ(5×3)
面積: 15.00, 周囲長: 16.00
円を描画: 中心(2, 2), 半径3
面積: 28.27, 周囲長: 18.85
========================
総面積: 43.27
総周囲長: 39.85
問題11
Productクラスを抽象クラスとして作成し、以下のメソッドを定義してください。
- check_inventoryメソッド
- update_inventoryメソッド
- can_sellメソッド
その後、Productクラスを継承して以下のクラスを作成してください。
- PhysicalProductクラス
- DigitalProductクラス
PhysicalProductクラスでは以下の情報を管理してください。
- 商品ID
- 商品名
- 価格
- 在庫数
- 重量
また、配送コストを計算するメソッドを作成してください。
DigitalProductクラスでは以下の情報を管理してください。
- 商品ID
- 商品名
- 価格
- ファイルサイズ
- ダウンロード制限回数
- ダウンロード回数
InventoryManagerクラスを作成し、複数の商品を管理してください。
以下のデータを使用してください。
- PhysicalProduct(“P001”, “ノートパソコン”, 150000, 10, 2.5)
- DigitalProduct(“D001”, “電子書籍”, 2000, 50, 3)
最後に以下の処理を実行してください。
- 商品を追加する
- 在庫レポートを表示する
- 商品を販売する
- 最終在庫レポートを表示する
販売処理では以下を実行してください。
- ノートパソコンを2個販売
- 電子書籍を1個販売
実行結果:
在庫数: 12
重量: 2.5kg
在庫状態: 12
---
ID: D001
商品名: 電子書籍
価格: 2000円
ファイルサイズ: 50MB
ダウンロード制限: 3回
現在のダウンロード回数: 1
在庫状態: 2回
---
===================
問題12
抽象クラスを利用して、複数種類の通知を管理できる通知システムを作成してください。通知には「メール通知」「SMS通知」「プッシュ通知」があり、それぞれ送信方法が異なります。すべての通知は共通して、受信者・メッセージ・送信状態を持つようにしてください。また、通知をまとめて管理するクラスを作成し、一括送信や統計表示ができるようにしてください。
各通知クラスでは send() メソッドをオーバーライドし、通知ごとの送信内容を表示してください。
送信後は送信日時と状態を更新してください。最後に、複数の通知を作成して管理クラスへ登録し、一覧表示・統計表示・一括送信を実行してください。
ABCと@abstractmethodを利用します。EmailNotification、SMSNotification、PushNotificationの3つの子クラスを作成します。send()をオーバーライドし、通知ごとの送信内容を表示します。- 通知をまとめて管理する
NotificationManagerクラスを作成します。 - 複数の通知オブジェクトを作成し、マネージャークラスへ登録します。
- 通知一覧を表示した後、実際に一括送信を実行し、送信後の状態を確認します。
実行結果:
通知を追加しました: メール通知
通知を追加しました: SMS通知
通知を追加しました: プッシュ通知
=== すべての通知 ===
1. 通知タイプ: メール通知
受信者: user@example.com
メッセージ: お知らせがあります
ステータス: 準備中
差出人: noreply@company.com
件名: 重要なお知らせ
2. 通知タイプ: SMS通知
受信者: 山田太郎
メッセージ: 本日15時にお伺いします
ステータス: 準備中
電話番号: 090-1234-5678
3. 通知タイプ: プッシュ通知
受信者: user123
メッセージ: 新しいメッセージが届きました
ステータス: 準備中
アプリ名: MyApp
デバイストークン: device_token_abc123
=== 通知統計 ===
メール通知: 1件
SMS通知: 1件
プッシュ通知: 1件
総送信済み数: 0件
================
=== 一括通知送信 ===
メール送信: noreply@company.com -> user@example.com
件名: 重要なお知らせ
本文: お知らせがあります
---
SMS送信: 090-1234-5678
メッセージ: 本日15時にお伺いします
---
プッシュ通知送信: MyApp
デバイス: device_token_abc123
メッセージ: 新しいメッセージが届きました
---
3件の通知を送信しました
=== 通知統計 ===
メール通知: 1件
SMS通知: 1件
プッシュ通知: 1件
総送信済み数: 3件
================
初級問題
問題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()