プログラミングを始めたばかりだとわかりづらい「クラスの継承」について、使いどころを含めてまとめてみます。
クラスとは
クラスは、データとそれを操作するメソッド(関数)をひとまとめにしたものです。
例えば、犬をプログラムで表現する場合、犬の属性(名前、年齢など)と行動(吠える、走るなど)を一つのクラスとして定義できます。
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
def bark(self):
print("ワンワン!")
継承とは
継承は、既存のクラスのプロパティやメソッドを新しいクラスが引き継ぐことを可能にする機能です。これにより、コードの再利用性が向上し、効率的なプログラムが書けるようになります。
継承元のクラスには、親クラス、基底クラス、スーパークラスといった呼び方があり、継承先のクラスはそれぞれの反対で、子クラス、派生クラス、サブクラスと呼ばれます。
サンプルコード①
base.py
には、基本となるクラスが定義されているとします。ここでは、Animal
クラスを基本として、それを継承して新しいクラスを作ります。
# base.py
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
raise NotImplementedError("サブクラスで実装してください")
Animal
クラスは、名前を持ち、話す機能があるが、具体的な話し方はサブクラスで定義される必要があります。
次に、Animal
クラスを継承して、犬と猫のクラスを作ります。
# dog.py
from base import Animal
class Dog(Animal):
def speak(self):
return f"{self.name}がワンワンと鳴きます。"
# cat.py
from base import Animal
class Cat(Animal):
def speak(self):
return f"{self.name}がニャーニャーと鳴きます。"
これらのクラスはAnimal
のspeak
メソッドをオーバーライド(上書き)して、犬と猫に合わせた振る舞いを定義しています。
サンプルコード②
親クラスにメソッドの実装がある場合のオーバーライドを書いてみます。
Animal
クラスに具体的なメソッドが定義されていて、その機能を継承しつつ拡張または変更したい場合、子クラスでそのメソッドをオーバーライドします。
親クラスのメソッドを子クラスのメソッドから呼び出すには、super()
関数を使用します。
以下のbase.py
では、Animal
クラスがintroduce
メソッドを持っていると仮定しています。このメソッドは動物が自己紹介をする基本的な処理を行います。
# base.py
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
print("何か音を出します。")
def introduce(self):
print(f"こんにちは、私の名前は{self.name}です。")
Dog
クラスでは、introduce
メソッドをオーバーライドして、犬特有の挨拶を加えてみます。
super()
を使用して親クラスのintroduce
メソッドを呼び出し、それに追加の処理を加えます。
# dog.py
from base import Animal
class Dog(Animal):
def speak(self):
print(f"{self.name}がワンワンと鳴きます。")
def introduce(self):
super().introduce() # 親クラスのメソッドを呼び出す
print("私は忠実な友達です!")
Dog
クラスのintroduce
メソッドでは、まずAnimal
クラスのintroduce
メソッドで基本的な自己紹介を行い、その後に犬としての特性を説明する追加の文言を出力します。
では、コードを1ファイルにまとめて実行してみます。
# base.py
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
print("何か音を出します。")
def introduce(self):
print(f"こんにちは、私の名前は{self.name}です。")
# dog.py
class Dog(Animal):
def speak(self):
print(f"{self.name}がワンワンと鳴きます。")
def introduce(self):
super().introduce() # 親クラスのメソッドを呼び出す
print("私は忠実な友達です!")
# メインの実行部分
if __name__ == "__main__":
my_dog = Dog("レックス")
my_dog.speak() # 犬が鳴く
my_dog.introduce() # 犬が自己紹介する
上記コードの実行結果は以下のとおりです。
レックスがワンワンと鳴きます。
こんにちは、私の名前はレックスです。
私は忠実な友達です!
Dog
クラスでspeak
メソッドをオーバーライドしているため、Dog
クラスのインスタンスでspeak
メソッドを呼び出した場合、親クラスAnimal
のspeak
メソッドが直接呼び出されることはありません。そのため、”何か音を出します。” という出力はされず、オーバーライドされたDog
クラスのspeak
メソッドにより “レックスがワンワンと鳴きます。” のみが出力されます。
継承の使いどころ
継承は汎化を表現するために使うと考えてよいと思います。
汎化
汎化は、共通の属性や振る舞いを持つ複数のクラスから一般的なクラスを抽出するプロセスです。
この一般的なクラスは「基底クラス」や「親クラス」と呼ばれます。前述のように、「動物」という親クラスを作り、その特性を共有する「犬」や「猫」といった子クラスを派生させることが汎化の一例です。
特化
特化は汎化の逆で、一般的なクラスから具体的なクラスを派生させるプロセスです。
これにより、基底クラスの基本的な特性を保ちつつ、新しい特性や振る舞いを追加して、より具体的なクラスを作成します。
子クラスは基底クラスの属性やメソッドを「継承」することで、コードの再利用性を高めると同時に、新しい機能を追加して個別のニーズに応えます。
継承と汎化・特化の関係
継承を使うことで、プログラムはより整理され、複数のクラス間でコードを効率的に共有できます。
基底クラス(汎化されたクラス)に共通の機能を定義し、そのクラスを継承することで、特定のクラス(特化されたクラス)に特有の振る舞いを追加することができます。
これにより、プログラム全体の構造が明確になり、各クラスの目的がはっきりとします。
継承の利点
単純にAnimal
クラスだけで動物に関するプログラムを実装する場合と比較して、クラスの継承を利用することにはいくつかメリットがあります。
コードの再利用
クラスの継承は、既存のコードを再利用することを可能にします。
たとえば、Animal
クラスに動物が共通して持つ属性やメソッド(例えば、名前やintroduce
メソッド)を定義しておけば、これを基礎としてさまざまな動物のクラス(犬、猫、鳥など)を作成する際に、その属性やメソッドを再度定義する必要がありません。
これにより、開発時間の短縮とエラーの削減が期待できます。
コードの整理と管理
継承を使うことで、似たような機能を持つオブジェクトを階層化して整理することができます。プログラムの各部分がどのように関連しているのかが明確になり、コードの読みやすさや保守のしやすさが向上します。
例えば、Dog
や Cat
クラスが Animal
クラスから継承されていることを知っていれば、これらのクラスが共通の振る舞いを持つことがすぐに理解できます。
柔軟性の向上
継承を利用することで、既存のクラスの振る舞いを変更または拡張することが容易になります。
子クラスでは、必要に応じて親クラスのメソッドをオーバーライドして新しい機能を追加したり、既存の機能を改善することができます。
これにより、アプリケーションが新しい要求に柔軟に対応できるようになります。
多様性の表現
実世界のオブジェクトや概念は多様であり、その多様性をプログラム内で表現する際に継承は非常に有効です。
例えば、Animal
クラスを基にして、さまざまな特有の特徴や行動を持つ多くの動物クラスを作成することができます。
ポリモーフィズムの活用
継承を使うことで、異なるクラスのオブジェクトが同じインターフェース(メソッドなど)を共有することができ、同一のメソッド呼び出しで異なる振る舞いを実現することができます(ポリモーフィズム)。
プログラム全体の柔軟性と拡張性を高めるのに役立ちます。
おわりに
クラスの継承は、複雑なプログラムを効率的に管理できます。Pythonでは、この概念を比較的簡単に学ぶことができるため、利用したいものです。