こんにちは、Tech Samuraiです!
前回の記事「【Python中級への道 #8】オブジェクト指向をはじめよう!クラスとインスタンスの概念」では、クラスという「設計図」からオブジェクトという「実物」を作る方法を学びましたね。
前回作成したPlayer
クラスは、どんなキャラクターにも共通する基本的な機能を持っていました。しかし、ゲームの世界には「戦士」や「魔法使い」といった、より専門的な職業が存在します。戦士は「怒りゲージ」を持ち、魔法使いは「MP」と「呪文を唱える」能力を持っているかもしれません。
これらの新しいクラスを作るたびに、Player
クラスのコードをコピー&ペーストするのは賢い方法ではありません。そこで登場するのが、OOPの真髄とも言える**「継承(けいしょう)」**という仕組みです。
継承を使えば、既存のクラスの能力(属性とメソッド)をまるごと引き継いだ、新しいクラスを簡単に作ることができます。さあ、クラスを親子のように関係付け、コードを再利用し、より複雑な世界を創造する冒険に出ましょう!
継承とは? 親と子の関係
継承とは、あるクラス(**親クラス**)の性質を、別のクラス(**子クラス**)が受け継ぐ仕組みのことです。
例えるなら… 生き物の進化です。
- 「哺乳類」という親クラスは、「体温を保つ」「乳で子を育てる」といった基本的な属性やメソッドを持っています。
- 「犬」や「猫」という子クラスは、「哺乳類」の性質をすべて**継承**します。そのため、犬も猫も体温を保ちます。
- さらに、子クラスは独自の性質を追加します。「犬」クラスは「ワンと吠える」メソッドを持ち、「猫」クラスは「ニャーと鳴く」メソッドを持っています。
このように、子クラスは親クラスの能力をすべて受け継いだ上で、自分だけの新しい能力を追加したり、一部の能力を自分流に変化させたり(オーバーライド)できるのです。
Pythonでの継承の書き方
Pythonで継承を行うのは非常に簡単です。クラスを定義する際に、クラス名の後の括弧 `( )` の中に、親クラスの名前を書くだけです。
class 子クラス名(親クラス名):
# 子クラス独自の定義
pass
それでは、前回のPlayer
クラスを親として、まずは空っぽのWarrior
(戦士)クラスを作ってみましょう。
# 親クラスとなるPlayerクラス(前回のおさらい)
class Player:
def __init__(self, name, hp, attack_power):
self.name = name
self.hp = hp
self.attack_power = attack_power
def attack(self, target_name):
print(f"{self.name} の攻撃!")
print(f"{target_name} に {self.attack_power} のダメージを与えた!")
# Playerクラスを継承したWarriorクラス
class Warrior(Player):
pass # passは何もしないという意味。今は空っぽにしておく。
# --- 使ってみよう ---
# Warriorクラスからオブジェクトを生成
conan = Warrior("剣士コナン", 150, 25)
# Warriorクラスにはattackメソッドを定義していないのに…
# 親であるPlayerクラスから引き継いでいるので、問題なく使える!
conan.attack("ドラゴン")
print(f"{conan.name}のHPは{conan.hp}です。")
実行結果:
剣士コナン の攻撃!
ドラゴン に 25 のダメージを与えた!
剣士コナンのHPは150です。
驚きましたか? `Warrior`クラスの中身は空っぽなのに、ちゃんとattack
メソッドが使えました。これこそが継承の力です!
子クラスの拡張とオーバーライド
子クラスが親と全く同じでは意味がありません。ここからは、子クラスに独自の機能を追加したり、親の機能を上書きしたりする方法を見ていきましょう。
子クラスに新しい属性とメソッドを追加する
戦士`Warrior`に、独自の属性「怒りゲージ(`rage`)」と、独自のメソッド「突撃 (`charge`)」を追加してみます。
class Warrior(Player):
def __init__(self, name, hp, attack_power, rage_point):
# まず、親クラスの__init__を呼び出して、共通の属性(name, hp, ap)を設定してもらう
super().__init__(name, hp, attack_power)
# その後で、Warrior独自の属性を追加
self.rage_point = rage_point
# Warrior独自のメソッド
def charge(self, target_name):
print(f"{self.name} の突撃! {target_name} に突っ込んでいく!")
# Warriorを生成
conan = Warrior("剣士コナン", 150, 25, 100)
conan.attack("ドラゴン") # 親から継承したメソッド
conan.charge("ドラゴン") # 自分だけのメソッド
print(f"怒りゲージ: {conan.rage_point}") # 自分だけの属性
【重要ポイント:super()
】
子クラスで__init__
を定義する際、super().__init__(...)
というおまじないを最初に書きます。これは、「**親クラスの初期化処理を、まず実行してください**」という意味です。これを実行することで、self.name
やself.hp
といった共通の属性が正しく設定されます。
親クラスのメソッドを上書き(オーバーライド)する
魔法使い`Magician`クラスを作り、攻撃方法を親の`Player`とは違う「魔法」にしてみましょう。親クラスと同じ名前のメソッドを子クラスで再定義することを**オーバーライド**と言います。
class Magician(Player):
def __init__(self, name, hp, attack_power, mp):
super().__init__(name, hp, attack_power) # 親の__init__を呼び出し
self.mp = mp
# 親のattackメソッドを「上書き」する
def attack(self, target_name):
print(f"{self.name} は呪文を唱えた!")
print(f"ファイアボールが {target_name} を襲う!")
print(f"{target_name} に {self.attack_power * 1.5} の大ダメージ!")
# Magicianを生成
merlin = Magician("魔法使いマーリン", 80, 10, 200)
# Magicianのattackを呼び出すと、オーバーライドした方が実行される
merlin.attack("ゴーレム")
print(f"MP: {merlin.mp}")
同じ`attack()`という呼び出しでも、オブジェクトが`Warrior`か`Magician`かによって、その振る舞いが変わるのが分かりますね。
まとめ
今回は、オブジェクト指向プログラミングの真髄である「継承」について探検しました。
- 継承は、**親クラス**の能力を**子クラス**が引き継ぐ仕組みであること。
- 書き方は
class 子クラス(親クラス):
とシンプル。 - 子クラスは、
super()
を使って親の能力を呼び出しつつ、独自の**属性やメソッドを追加**できること。 - 子クラスは、親のメソッドを再定義して、振る舞いを変更(オーバーライド)できること。
継承をマスターしたことで、あなたはコードの再利用性を最大限に高め、拡張性の高い、美しいプログラムを設計する能力を手に入れました。
さて、クラスという設計図でデータと処理をまとめ、継承でその関係性を定義できるようになりました。しかし、クラスの内部のデータを、外から不用意に書き換えられてしまう危険性がまだ残っています。次回は、クラスのデータを安全に守るための「壁」を作る、**「カプセル化」**という概念を探検します。お楽しみに!
コメント