Pythonでクラスを学び始めると、必ず出会うのが「オブジェクト指向」と「継承」という考え方です。 聞き慣れない言葉に少し身構えてしまうかもしれませんが、心配はいりません。継承はすでにあるクラスの良いところを引き継いで、新しいクラスを効率よく作る仕組みのこと。
イメージとしては親の性質を受け継ぐ子どもを作る感じです。
クラスをいくつも書いていると「同じコードを何回もコピペしてる…」と感じる瞬間はありませんか?
そんなときに継承が大きな力を発揮します。
継承は、既存のクラス(親クラス・基底クラス)を土台にして、新しいクラス(子クラス・サブクラス)を作る方法です。 親クラスに書いた共通の属性やメソッドは、子クラスでもそのまま使えます。
必要であれば、子クラス側で機能を追加したり、振る舞いを上書き(オーバーライド)したりできます。
なぜこれが嬉しいのでしょうか?
理由はシンプルで、同じ処理を何度も書かなくてよくなるからです。 共通部分は親クラスに1回書く。それを引き継ぎながら、子クラス側では差分だけ書けばOK。 これによりコードの重複が減り、読みやすさ(可読性)と拡張性がぐっと上がります。
コピペの繰り返しに疲れていませんか?継承はそんな悩みを解決します。
親クラスに共通機能をまとめておけば、子クラスで再利用できるため、無駄がなくなります。 さらに、親クラスをベースに新しい仕様を持つクラスを簡単に増やせるので、アプリが大きくなっても管理しやすくなります。
もうひとつ大切なのは、設計が自然と整理されることです。 「Animal(動物)→ Dog(犬)」「Vehicle(乗り物)→ Car(車)」のように、現実の分類に近い階層でクラスを並べられるため、後から読む人にも意図が伝わりやすくなります。
Pythonで継承を使うときの構文はとてもシンプルです。 子クラス名の後ろの丸かっこの中に、親クラス名を書くだけです。
class 子クラス名(親クラス名):
# 子クラス独自の属性やメソッドを定義
...
これだけだとイメージがつかみにくいかもしれません。 具体例で確認してみましょう。
次のコードでは、Animal を親クラスとして定義し、その性質を受け継ぐ Dog と Cat を作成しています。 動物は「名前」を持ちますが、鳴き声は動物ごとに違うため、親クラスの段階では「ここは子クラスで実装してね」という合図だけを置いておきます。
# 親クラス: 動物
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
raise NotImplementedError("このメソッドはサブクラスで実装する必要があります。")
# 子クラス: 犬
class Dog(Animal):
def speak(self):
return f"{self.name}はワンワンと鳴きます。"
# 子クラス: 猫
class Cat(Animal):
def speak(self):
return f"{self.name}はニャーニャーと鳴きます。"
# インスタンスを作成
dog = Dog("ポチ")
cat = Cat("ミケ")
# メソッドを呼び出す
print(dog.speak()) # ポチはワンワンと鳴きます。
print(cat.speak()) # ミケはニャーニャーと鳴きます。
まず、Animal は「すべての動物が共通で持つ性質」を表す親クラスです。このクラスでは、インスタンスが必ず name を持つように初期化処理(init)が定義されています。 一方、鳴き声の出し方は動物ごとに異なります。そこで、speak は親クラスでは具体的な処理を書かずに NotImplementedError を発生させ、「子クラスでこのメソッドを用意してね」という明確なサインにしています。
Dog と Cat は Animal を継承しているため、name を受け継いでいます。これにより、Dog("ポチ") のように名前を渡すだけで、Dog インスタンスは自然に name を持てます。そして、両クラスは speak を自分たちの鳴き声で上書き(オーバーライド)しています。結果として、同じ speak という呼び出しでも、犬はワンワン、猫はニャーニャーと返すようになります。「Dog は Animal の一種(is-a)」という関係が、コードの形でも表現できています。
「子クラスって必ずメソッドを上書きしないといけないの?」と疑問に思うかもしれません。
答えはノーです。親クラスにすでに便利なメソッドがあるなら、子クラス側は何も書かなくても、それをそのまま使えます。 例えば次のように、info という共通メソッドを親に置いておくと、子クラスは何も追加しなくても動きます。
class Animal:
def __init__(self, name):
self.name = name
def info(self):
return f"{self.name}は生きています。"
class Dog(Animal):
pass # 何も追加しない
dog = Dog("ポチ")
print(dog.info()) # ポチは生きています。
この例では、Dog は Animal を継承しているだけですが、親クラスの info をそのまま呼び出せています。 継承の再利用という強みがよくわかるはずです。
初めて継承を使うと、NotImplementedError にびっくりすることがあります。
これはエラーというより「このメソッドは子クラスで用意してね」というリマインドです。 Animal.speak を呼んだのに Dog や Cat 側で speak を定義し忘れていると発生します。
もし見かけたら、「あ、オーバーライドが必要なんだな」と受け止めて、子クラスに実装を追加しましょう。
また、「子クラスでも初期化が必要になったらどうするの?」という疑問も自然です。
たとえば Dog 独自の属性を増やす場合は init を上書きしますが、親の初期化内容も活かしたい場面が多いでしょう。 そんなときは super() を使います。
super の具体的な使い方は後の「super() 関数の使い方」の章で丁寧に扱いますので、ここでは「親の処理を呼び出すための仕組みがある」と覚えておけば十分です。
継承は、Python のオブジェクト指向における基本中の基本です。
親クラスに共通のロジックを集約し、子クラスがそれを引き継いで使いながら、必要な部分だけを上書き・追加していく。 これにより、コードの再利用性が高まり、読みやすく拡張しやすい設計を実現できます。
もし今、似たようなクラスを複数作っていて「どこを直せば全部に反映されるんだろう…」と感じているなら、継承を取り入れるタイミングです。
次のセクションでは、今回少し触れた「オーバーライド」や「super()」など、継承をさらに使いこなすためのテクニックを順番に学んでいきましょう。