では、Studentクラスに新たな属性を追加してみよう。Studentクラスに何が必要だろうかと考えて、ここではシンプルに学校名をインスタンス変数schoolとして追加することにしよう。インスタンス変数に値を設定するには、もちろん__init__メソッドを定義すればよい。つまり、Studentクラスのインスタンスを生成するには、名前と年齢、学校名の3つの引数を指定する必要があるということだ。これら(とインスタンス自身を参照するself)が__init__メソッドのパラメーターとなる。年齢と同様に、学校名を取得するメソッドも追加しておこう。実際のコードは次のようになる。
class Student(Person):
def __init__(self, name, age, school):
self.name = name
self.age = age
self.school = school
def get_school(self):
return self.school
ここでは、StudentクラスでPersonクラスにもある(さらにいえば、objectクラスにもある)__init__メソッドを定義しているが、このように基底クラスで定義されているものと同名のメソッドを派生クラスで定義することを「メソッドをオーバーライド(上書き)する」という。
__init__メソッドをオーバーライドするときに注意したいのは、「Student(……)」のようにしてStudentクラスのインスタンスを生成すると、Pythonによって__init__メソッドが自動的に呼び出されるが、この際に基底クラスであるPersonクラスの__init__メソッドは呼び出されなくなってしまう点だ(他のプログラミング言語では、派生クラスのインスタンスを生成すると、芋づる式に基底クラスの「コンストラクタ」が呼び出される仕組みになっているものが多いが、Pythonではそうはなっていない)。
簡単にいうと、Personクラスの__init__メソッドで行っていたインスタンスの初期化処理は行われなくなってしまうということだ。そのために上では、Personクラスのインスタンス変数とStudentクラスのインスタンス変数の3つを初期化するようになっている。
簡単なプログラムであればこのようにしても問題ないが、基底クラスの__init__メソッドで複雑な処理をしていて、それを省略してしまうことに問題がある場合などには、「派生クラスの__init__メソッド内部で基底クラスの__init__メソッドを明示的に呼び出す」ようにする。これにはsuper関数を引数なしで呼び出すことで、基底クラスのメソッドにアクセスできるようになる。
super()
super関数を呼び出しているクラスの基底クラスのメソッドにアクセス可能にするオブジェクトを返す。
これを使うと先ほどのコードは次のように書ける。
class Student(Person):
def __init__(self, name, age, school):
super().__init__(name, age)
self.school = school
def get_school(self):
return self.school
このコードでは、super関数の戻り値を介して、Personクラスの__init__メソッドを呼び出して、そこにStudentクラスの__init__メソッドのパラメーターnameとageに渡された値を渡すことで、基底クラスで定義されるインスタンス変数の初期化を行っている。ちなみに、派生クラスで__init__メソッドが定義されていなければ、基底クラスの__init__メソッドが呼び出される(そのため、クラス定義の本体がpass文になっていたときには、問題なく2つのインスタンス変数が初期化されていたということだ)。
では、上のクラス定義に修正した上で、Studentクラスのインスタンスを作ってみよう。
isshiki = Student('isshiki', 18, 'Imperial univ')
isshiki.hello()
print(isshiki.get_school())
実行すると、次のようになる。
最後にhelloメソッドも「オーバーライド」してみよう。
class Student(Person):
def __init__(self, name, age, school):
super().__init__(name, age)
self.school = school
def get_school(self):
return self.school
def hello(self):
super().hello()
print('You are a student of', self.school)
ここでは先ほどと同様にsuper関数を介して、基底クラスのhelloメソッドを呼び出して、その後、Studentクラスに追加したインスタンス変数schoolを使った出力を行うようにしている。
これまでと同様に、以下のコードでその動作を確認してみよう。
isshiki = Student('isshiki', 18, 'Imperial univ')
isshiki.hello()
実行結果を以下に示す。
このように、super関数は__init__メソッド以外にも、派生クラスでオーバーライドしたメソッドから基底クラスの同名のメソッドを明示的に呼び出すのに使用できる(オーバーライドしていないメソッドについては、「self.メソッド(……)」などのようにして呼び出せるので、super関数を使う必要はない)。
プログラムを書いていると、あるインスタンスが何かのクラスやその派生クラスのインスタンスかどうかや、あるクラスが別のクラスの派生クラスかどうかを調べたくなることがある。これを調べてくれるのが以下の2つの関数だ。
isinstance(obj, class)
パラメーターobjに受け取ったオブジェクト(インスタンス)がclassで指定されるクラスのインスタンス、またはその派生クラスのインスタンスである場合にTrueを、そうでない場合にFalseを返す。
パラメーター | 説明 |
---|---|
obj | パラメーターclassに指定されたクラスまたはその派生クラスのインスタンスかどうかを調べたいオブジェクト |
class | パラメーターobjに与えられたオブジェクトが、特定のクラスまたはその派生クラスのインスタンスかどうかを調べる対象。複数のクラスをタプルとして渡してもよい |
isinstance関数のパラメーター |
issubclass(class1, class2)
パラメーターclass1に受け取ったクラス(クラスオブジェクト、クラス名)がclass2で指定されるクラスまたはその派生クラスである場合にTrueを、そうでない場合にFalseを返す。
パラメーター | 説明 |
---|---|
class1 | パラメーターclass2に指定されたクラスまたはその派生クラスであるかどうかを調べたいクラスを指定する |
class2 | パラメーターclassに受け取ったクラスが、特定のクラスまたはその派生クラスであるかどうかを調べる対象。複数のクラスをタプルとして渡してもよい |
issubclass関数のパラメーター |
使用例を以下に示す。
# 整数値「100」はfloat型のインスタンスか
print('isinstance(100, float):', isinstance(100, float))
# studentはPersonクラスのインスタンスか
student = Student('isshiki', 18, 'Imperial univ')
print('isinstance(student, Person):', isinstance(student, Person))
# StudentクラスはPersonクラスの派生クラスか
print('issubclass(Student, Person):', issubclass(Student, Person))
# PersonクラスはPersonクラスの派生クラスか
print('issubclass(Person, Person):', issubclass(Person, Person))
# 「100」はfloatクラスまたはstrクラスのインスタンスか
print('isinstance(100, (float, str)):', isinstance(100, (float, str)))
# StudentクラスはPersonクラスまたはStudentクラスの派生クラスか
print('issubclass(Student, (Person, Student)):', issubclass(Student, (Person, Student)))
実行結果を以下に示す。
「issubclass(Person, Person)」の結果がTrueとなっている点には注意しよう。あるクラスはそのクラスのサブクラスと見なされる。
なお、オブジェクトの型を調べるには、type関数も使えるが、こちらはそのオブジェクトの型を教えてくれるだけで、それが特定のクラスの継承関係の中に含まれているかどうかまでは教えてくれない。そのため、type関数のドキュメントでは「オブジェクトの型の判定には、 isinstance() 組み込み関数を使うこと」が推奨されていることも付記しておこう。
今回はクラスの役割、クラスの継承の意味、継承の方法、メソッドのオーバーライド、オブジェクトの型を調べる方法など、クラスを継承することの基礎について見た。次回はこれらについてもう少し掘り下げて調べてみる予定だ。
「Python入門」
Copyright© Digital Advantage Corp. All Rights Reserved.