[Python入門]クラスの継承:Python入門(2/2 ページ)
Pythonにおけるクラスの役割、クラスを継承することの意味、継承の方法など、クラスの継承に関する基本知識を概観しよう。
派生クラスに属性を追加する:メソッドのオーバーライド
では、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()
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関数
isinstance(obj, class)
パラメーターobjに受け取ったオブジェクト(インスタンス)がclassで指定されるクラスのインスタンス、またはその派生クラスのインスタンスである場合にTrueを、そうでない場合にFalseを返す。
パラメーター | 説明 |
---|---|
obj | パラメーターclassに指定されたクラスまたはその派生クラスのインスタンスかどうかを調べたいオブジェクト |
class | パラメーターobjに与えられたオブジェクトが、特定のクラスまたはその派生クラスのインスタンスかどうかを調べる対象。複数のクラスをタプルとして渡してもよい |
isinstance関数のパラメーター |
issubclass関数
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() 組み込み関数を使うこと」が推奨されていることも付記しておこう。
まとめ
今回はクラスの役割、クラスの継承の意味、継承の方法、メソッドのオーバーライド、オブジェクトの型を調べる方法など、クラスを継承することの基礎について見た。次回はこれらについてもう少し掘り下げて調べてみる予定だ。
今回のまとめ:クラスの継承
- クラスは「データとそれを処理するコードをまとめて扱う」ための入れ物
- クラスを継承することで、既存のクラスの特性を受け継ぎながら、そこに新たな特性を付加できる
- クラスを継承する際に、元となるクラスを「基底クラス」「親クラス」「スーパークラス」などと呼び、機能を受け継ぐクラスを「派生クラス」「子クラス」「サブクラス」などと呼ぶ
- クラスを継承するにはクラスの定義時にかっこ「()」内に基底クラスを指定する
- 派生クラスでインスタンス変数の初期化を行うには__init__メソッドをオーバーライドする
- __init__メソッドをオーバーライドすると、基底クラスの__init__メソッドは呼び出されなくなる点に注意すること
- super関数を介して「super().メソッド(……)」とすることで、オーバーライドしたメソッド内で基底クラスの同名メソッドを呼び出せる
- あるオブジェクトが特定のクラスまたはその派生クラスのインスタンスであるかどうかはisinstance関数で調べられる
- あるクラスが特定のクラスまたはその派生クラスであるかどうかはissubclass関数で調べられる
「Python入門」
Copyright© Digital Advantage Corp. All Rights Reserved.