[Python入門]多重継承とmixin:Python入門(2/2 ページ)
多重継承を行う際には複数のクラスからインスタンス変数を継承すると問題が発生する。それを回避する方法と、そこから生まれるmixinという考え方を紹介する。
mixinされたクラスの挙動を変更する
今見たのはシンプルな例だったが、クラスごとに独自の振る舞いをmixinにも取り込みたいことがあるかもしれない。つまり、mixinクラスのメソッドでは、「何らかの処理を実行する大枠を定義しておき、mixinした側のクラスでそのクラスに独自の振る舞いを実現する」という方法がある(一般に「テンプレートメソッドパターン」などと呼ばれる)。
class Util:
def mixin_method(self):
self.another_method()
def another_method(self):
raise NotImplementedError('method not implemented')
このUtilクラスでは2つのメソッドが定義されている。mixin_methodメソッドは、another_methodメソッドを呼び出すだけだ。そして、another_methodメソッドではエラー(NotImplementedError例外)を発生させるだけだ。エラーとするだけの関数を定義しているのは、mixin_methodメソッドからはanother_methodメソッドを呼び出すが、それはUtilクラスをmixinした側でオーバーライドする必要があることを伝えるためだ。
では、これを幾つかのクラスでmixinしてみよう。
class Foo:
pass
class Bar(Foo, Util):
def __init__(self):
self.x = 'BAR'
def another_method(self):
print('Hello from', self.x)
class Baz(Foo, Util):
def __init__(self):
self.y = 'BAZ'
def another_method(self):
print('Hello from', self.y)
class Qux(Foo, Util):
pass
Bar/Baz/Quxのうち、最初の2つではUtilクラスで定義されているanother_methodメソッドをオーバーライドしている。これらが実際にはmixin_methodメソッドから呼び出される。このようにすることで、クラスに固有の振る舞いをUtilクラスからmixinする機能に組み込める。Quxクラスではオーバーライドをしていない。
では、実際の動作を見てみよう。
bar = Bar()
baz = Baz()
qux = Qux()
bar.mixin_method()
baz.mixin_method()
qux.mixin_method()
これらを実行すると次のようになる。
mixin_methodメソッドを呼び出したことにより、これを経由して、BarクラスとBazクラスで定義したanother_methodメソッドが呼び出されたことと、another_methodメソッドをオーバーライドしなかったQuxクラスのインスタンスではmixin_methodメソッドを呼び出すことでエラー(NotImplementedError例外)が発生したことと分かる。
このように、mixinクラスの側では処理の大枠を定義して、詳細な部分をmixinする側で定義するといった使い方もできる。
mixinクラスを定義する際の注意点
既に書いたが、mixinクラスは独立して存在することができない。つまりそのクラスから直接インスタンスを生成しても役には立たないことには注意しよう。例えば、上で定義したUtilクラスのインスタンスを生成してみよう。
util = Util()
util.mixin_method()
実行結果は次のようになる。
mixin_methodメソッドを呼び出したので、その中からanother_methodメソッドが呼び出されて、例外が発生している。上で見たUtilクラスは他のクラスでanother_methodメソッドがオーバーライドされることを前提としているので、ここでは例外が発生した。
しかし、mixinクラスが単体ではそれほど役には立たないことは、前ページで見たUtilクラスでも同じことがいえる。
class Util:
def show_members(self):
print(self.__dict__)
def show_mro(self):
print(self.__class__.__mro__)
このUtilクラスに対して、インスタンスを生成して、メソッドを呼び出した結果を以下に示す。
先ほどとは異なり、エラーは発生していないが、実行しても意味があるとは思えない結果しか得られていないことが分かるだろう。
多くの場合、mixinクラスは他のクラスでmixinされることを念頭に置いて設計される。そのため、独立してそのインスタンスを生成したところであまり意味はない。他のクラスから利用される、また、利用する側のクラスと連携させることなどを考えると、mixinクラスをどのような定義にするかは慎重に考える必要がある。
実装の継承と仕様の継承
Pythonにおけるmixinは、あるクラスで定義されている機能(メソッド)を他のクラスで利用するための方法論といえる。これを「実装の継承」「実装の再利用」などと呼ぶことがある(「実装」とは「ある機能や処理を実際に何らかのプログラミング言語で記述する」こと。つまり、「実装の継承」とは「あるクラスで既に書かれているコードを別のクラスで再利用する」ことを意味する)。
これに対して、「仕様の継承」という言葉もある。「仕様」とは「その機能の名前や処理に必要なデータ」などを定めたもので、実際に処理をするコード(実装)は仕様には含まれない。「仕様」に沿った処理は、仕様を継承したクラスで実装する必要がある。C#などの言語では、これを「インタフェース」と呼ばれる機構でサポートしている。
多重継承をサポートしていない言語の多くでは、多重継承の代わりに、単一のクラスを継承すると共にインタフェースで定められた「仕様」を「実装」することで、多重継承と似た機能を実現している(インタフェースをサポートする言語の一つであるC#では、バージョン8でインタフェースで定義されるメソッドに実装を持たせられるようにもなる予定だ)。
まとめ
今回は多重継承時に複数のクラスからインスタンス変数を継承したときの問題や、その解決策、mixinと呼ばれる多重継承の方式などについて見た。クラスはここまでとして、次回はこれまでにエラーとだけ述べていた例外について見ていこう。
今回のまとめ:
- 多重継承時に複数のクラスからインスタンス変数を受け継ぐと、問題が発生する場合がある
- そこで多重継承時には、インスタンス変数を継承するクラスは1つだけで、他にはメソッドのみを定義したクラスだけを継承するという方法がある
- メソッドのみを定義したクラスとは、何らかの機能だけを持つクラスといえる
- このような多重継承のやり方を「mixin」と呼ぶ
- mixinでは基底クラス→派生クラス→……という継承構造を1つに限定し(インスタンス変数を持つクラスがこれに相当する)、何らかの機能だけを提供するクラスを、その継承構造とは無関係に組み込むイメージになる
- mixinクラスは他のクラスにmixinされることを前提としているので、単独でインスタンスを生成してもあまり意味はない
- mixinクラスではそのメソッドで処理の大枠を決定し、詳細をmixinする側のクラスに任せるといった使い方も可能
Copyright© Digital Advantage Corp. All Rights Reserved.