▶ クラスについて基本から学びたい方は以下の記事を参考にしてください。
はじめに
今回はクラスを継承して作った際に、機能追加や機能変更する方法を基本から解説します。そもそもスーパークラスを継承して作ったクラスが、スーパークラスと全く同じ機能であると意味がないですよね。そこで、一部の機能を追加・変更する方法が提供されています。それがオーバーライドという考え方です。
オーバーライド
継承してクラスを作った場合、スーパークラスのメソッドはサブクラスにそのまま受け継がれるのでした。機能を変更したいメソッドだけ、サブクラスであらためて定義するようにします。これをメソッドのオーバーライドと呼びます。
初期化メソッドのオーバーライド
前回はスーパークラスを継承してサブクラスを作る簡単な例を紹介しました。サブクラスでは何も定義せずに(処理部分をpassとした)スーパークラスの機能が使えることを確認しました。
サブクラスに初期化メソッド自体も記載しないと、自動的にスーパークラスの初期化メソッドが呼ばれます。逆に、サブクラスに初期化メソッドがあると、スーパークラスの初期化メソッドは呼ばれなくなります。まず、これを試してみましょう。
# クラスAnimalの作成
class Animal:
def __init__(self, name,age):
self.name = name
self.age = age
print('Animal クラスのinitが呼び出されました')
# AnimalをベースにしてクラスRakudaを作成
class Rakuda(Animal):
pass
ベースとするクラス(=スーパークラスを)Animalとします。Animalの__init__には、呼び出されたことがわかるようにprint文で「Animalクラスのinitが呼び出されました」を記載しています。RakudaクラスはAnimalを継承して作成したクラスで、__init__は何も定義していない状態です。
Rakudaクラスを使って、インスタンスを作ってみましょう。
# Rakudaクラスからインスタンスを作る
rakuda = Rakuda('らくだ',28)
このようにスーパークラスの__init__が呼ばれていることが確認できました。実際、インスタンスを作る際にこれはわかっていて、スーパークラスの__init__でnameとageが必要になるので「Rakuda(’らくだ’,28)」としているわけですね。
では、次のようにサブクラスであるRakudaに__init__を書いてみましょう。
# クラスAnimalの作成
class Animal:
def __init__(self, name,age):
self.name = name
self.age = age
print('Animal クラスのinitが呼び出されました')
# AnimalをベースにしてクラスRakudaを作成
class Rakuda(Animal):
def __init__(self,name):
self.name = name
print('Rakuda クラスのinitが呼び出されました')
11行目~13行目にRakudaクラスの初期化メソッド__init__を定義しています。ここでは、インスタンス変数をnameのみとしています。また、呼び出されたことがわかるようにprint文で「Rakudaクラスのinitが呼び出されました」と記述しています。
ではインスタンスを作ってみましょう。先ほどと同じようにやってみます。
# インスタンスの作成
rakuda=Rakuda('らくだ',28)
これはエラーになります。初期化メソッドは2つの引数が必要だけど、3つも指定されているよ、問エラーですね。サブクラスのRakudaクラスではインスタンス変数はnameのみなので、selfとnameの引数のみでよいので、「Rakuda(‘らくだ’,28)」ではなく、「Rakuda(‘らくだ’)」としなければいけないのですね。
やり直してみましょう。
# インスタンスの作成
rakuda=Rakuda('らくだ')
今度はうまくいきました。Rakudaクラスの__init__が呼び出されていることも確認できましたね。先ほどの例ではスーパークラスの__init__が呼び出されるわけではないので、ageを指定しようとするとエラーとなったのですね。
スーパークラスの初期化メソッドを呼び出す
スーパークラスをベースとしたいから、継承でクラスを作成しているので、サブクラスで定義しているインスタンス属性を定義するのは無駄ですね。
スーパークラスで定義されているインスタンス属性で必要なものは流用して、変更・追加したいものをサブクラス内で新たに定義することにしましょう。
スーパークラスのメソッドを呼び出すには、組み込み関数であるsuper()を使います。次の書式で書きます。
サブクラスの__init__内で「super()」として続いて、呼び出したいメソッドを指定する形になっています。__init__()の中でスーパークラスで定義されているインスタンス属性で流用したいものを指定します。このとき、スーパークラスのインスタンスの初期化もかならずおこなう必要があります。
試してみましょう。
# クラスAnimalの作成
class Animal:
def __init__(self, name,age):
self.name = name
self.age = age
print('Animal クラスのinitが呼び出されました')
def introduce(self):
print(f'{self.name}は{self.age}歳です')
# AnimalをベースにしてクラスRakudaを作成
class Rakuda(Animal):
def __init__(self,name,age):
super().__init__(name,age)
print('Rakuda クラスのinitが呼び出されました')
14行目:サブクラス内で組み込み関数super()を使って、スーパークラスの__init__を呼び出しています。ここでスーパークラスから呼び出したいインスタンス変数も指定します。ここで指定した変数は13行目でも指定する必要があることに注意しましょう。
では、Rakudaクラスでインスタンスを作ってみましょう。
# インスタンスの作成
rakuda=Rakuda('らくだ',28)
これは、先のクラスを定義したコードの13~15行目でサブクラスで__init__を定義しています。14行目でまず、スーパークラスの__init__を呼び出しているので「Animalクラスのinitが呼び出されました」が表示され、次にサブクラスのinit内の15行目のprint文で「Rakudaクラスのinitが呼び出されました」が表示された、というわけですね。
このようにサブクラスにinitがある状態でもスーパークラスのinitを呼び出すことができるようになりました。さらに、サブクラスにインスタンス変数もを定義してみましょう。
# クラスAnimalの作成
class Animal:
def __init__(self, name,age):
self.name = name
self.age = age
print('Animal クラスのinitが呼び出されました')
def introduce(self):
print(f'{self.name}は{self.age}歳です')
# AnimalをベースにしてクラスRakudaを作成
class Rakuda(Animal):
def __init__(self,name,age,number):
super().__init__(name,age)
self.number = number
print('Rakuda クラスのinitが呼び出されました')
print(f'{self.name}のこぶの数は{self.number}です')
今回はRakudaクラス固有のインスタンス変数numberを加えてみました。これはらくだのコブの数を表す変数としましょう。15行目で定義しています。これまで同様にサブクラスの__init__の中で「self.インスタンス変数名」を書くことで定義できます。
インスタンスを作ってみましょう。
# インスタンスの作成
rakuda=Rakuda('らくだ',28,1)
うまくいきましたね。
メソッドのオーバーライド
スーパークラスを継承してサブクラスを作った場合には、通常、スーパークラスのメソッドがそのまま使えるのでした。おさらいしておきましょう。
先ほど作ったRakudaというサブクラスではメソッドを定義していません。Rakudaから作ったインスタンスであるrakudaで、スーパークラスで定義されたintroduceメソッドが動くことを確認してみましょう。
# スーパークラスで定義したメソッドの確認
rakuda.introduce()
ちゃんと動いています。では、サブクラスではこのメソッドを変更したい場合はどうしたらよいでしょうか?これは簡単です。サブクラス内で同名のメソッドを定義するだけです。
# クラスAnimalの作成
class Animal:
def __init__(self, name,age):
self.name = name
self.age = age
def introduce(self):
print(f'{self.name}は{self.age}歳です')
# AnimalをベースにしてクラスRakudaを作成
class Rakuda(Animal):
def __init__(self,name,age,number):
super().__init__(name,age)
self.number = number
def introduce(self):
print(f'{self.name}はこぶを{self.number}つ持っています。')
17-18行目でintroduceとうメソッドを定義しています。これは、スーパークラスで定義した7-8行目のメソッドと同じ名前です。
Rakudaクラスでインスタンスを作って、このメソッドを呼び出してみましょう。
# インスタンスの作成
rakuda=Rakuda('らくだ',28,1)
# スーパークラスで定義したメソッドの確認
rakuda.introduce()
どうでしょうか?introduceメソッドの実行結果は、スーパークラス内で定義した内容ではなく、サブクラス内で定義した内容になっています。これがメソッドのオーバーライドです。メソッド名を同名にするだけなので簡単ですね。
最後にスーパークラスのメソッドに機能を追加する場合もみておきましょう。スーパークラスで定義されている内容を、再度、サブクラスで定義するのは無駄ですよね。初期化メソッドのところでも扱ったように組み込み関数のsuper()でスーパークラスのメソッドを呼び出すことができます。
やってみましょう。
# クラスAnimalの作成
class Animal:
def __init__(self, name,age):
self.name = name
self.age = age
def introduce(self):
print(f'{self.name}は{self.age}歳です')
# AnimalをベースにしてクラスRakudaを作成
class Rakuda(Animal):
def __init__(self,name,age,number):
super().__init__(name,age)
self.number = number
def introduce(self):
super().introduce()
print(f'{self.name}はこぶを{self.number}つ持っています。')
サブクラスRakudaでは、17-19行目でintroduceメソッドを定義しています。18行目でsuper()関数を使ってスーパークラスのintroduce()関数を呼び出しています。このサブクラスRakudaでインスタンスを作ってintroduce()メソッドを実行してみましょう。
# インスタンスの作成
rakuda=Rakuda('らくだ',28,1)
# スーパークラスで定義したメソッドの確認
rakuda.introduce()
まず「らくだは28歳です」の部分は、スーパークラスで定義されたintroduceの処理内容が実行されていますね。これはサブクラスでのintroduce()を定義する際に、まずスーパークラスのintroduce()をsuper()関数で呼び出しているためです。
次に「らくはこぶを1つ持っています」の部分はサブクラスで定義された内容が実行されています。このようにsuper()関数でスーパークラスのメソッドを呼び出して利用することができます。
まとめ
今回はスーパークラスを継承してサブクラスを作る際のオーバーライドの仕組みについて解説しました。サブクラスに__init__がなければ、自動的にスーパークラスの__init__が呼ばれます。サブクラスに__init__がある場合は、これが優先されるためスーパークラスの__init__はdefaultでは呼ばれません。スーパークラスの__init__を呼び出すためには、組み込み関数のsuper()関数を使います。
同様にメソッドについても、継承した場合はスーパークラスのメソッドが使えます。但し、サブクラスに同名のメソッドがあった場合は、こちらが優先されます。スーパークラスのメソッドを呼び出したいときは、initの場合と同様にsuper()関数で呼び出すことができます。
コメント