LoginSignup
4
3

More than 3 years have passed since last update.

sklearnに見る、pythonでのクラス継承 実践編

Last updated at Posted at 2020-01-03

はじめに

Pythonでクラス継承をどう書くのか?について実践編を書きます。
なお基礎編はありません。

いつ重要?

ソフトウェア、大規模ライブラリを作る際に有用と思われます。
複数クラスで同じように定義するメソッドを、抽象クラスや鋳型となる親クラスに書いておけば子クラスで定義する必要がなくなります。

今回はみんな大好き scikit-learn のTransformerMixinを例に見ていきます。TransformerMixinとは、scikit-learnの随所で利用されているクラスです。scikit-learnでは前処理などでfittransformを使うことが多く、それらをまとめて行うfit_transformTransformerMixinに実装されています。

この機構により、を継承するだけでfit_transformが利用できるようになる、という非常にシンプルな例です。

参考までに、通常pythonではabc.ABCMetaを継承した抽象クラスの実装が行われます。Pythonで常備されているパッケージにabcというものがあり、抽象クラスの定義に利用されます。本稿末尾の参考文献もご確認ください。

abc — Abstract Base Classes

試したこと

以下の2点について試しました。書いていきます。

  1. ソースコードリーディング
  2. サンプルコード動かした

ソースコードリーディング

まず以下のコードを読んでみましょう。scikit-learnのgithubのページから引用しています。 scikit-learn TransformerMixin

base.py
...
class TransformerMixin:
    def fit_transform(self, X, y=None, **fit_params):
        ...
        if y is None:
        ...
            return self.fit(X, **fit_params).transform(X)
        else:
        ...
            return self.fit(X, y, **fit_params).transform(X)
...

Pythonで抽象クラスを定義する場合よく使われるのはabc.ABCMetaというクラスです。
しかし、上の例では、abc.ABCMetaを使うこと無く、メソッドだけ定義されています。

abc.ABCMeta@abc.abstractmethodを利用した抽象クラスでは、継承した先で実装を強要するクラスができます。
対して上記のTransformerMixinでは、共通のメソッドを全ての継承先で利用可能にしたいだけです。この場合は、abcは利用しなくて良いということです。

サンプルコード動かした

試し書きしてみます。abcを使った抽象クラスを利用すると、以下の様に動作します。なお、関数名等はアベコベですので気にしないでください...

$ cat sampleAbsclass.py
from abc import ABCMeta, abstractmethod
class DensityMixin(metaclass=ABCMeta):
    @abstractmethod
    def score(self, X, y=None):
        pass

class OneClassSvm(DensityMixin):
    def __init__(self):
        print('echo echo', 'hello hello')
    # def score(self, X, y=None):
    #     print(*X.shape)

def main():
    ocsvm = OneClassSvm()
    from numpy.random import randn
    X=randn(100, 10)
    ocsvm.score(X)
    return 0

if __name__=='__main__':
    main()
$ python sampleAbsclass.py
Traceback (most recent call last):
  File "sampleAbsclass.py", line 21, in <module>
    main()
  File "sampleAbsclass.py", line 14, in main
    ocsvm = OneClassSvm()
TypeError: Can't instantiate abstract class OneClassSvm with abstract methods score

抽象クラスのDensityMixin内に@abstractmethodデコレーター付きのメソッドscoreがあるのに、それを継承先のOneClassSvmで実装していないのはケシカラン!というエラーメッセージです。

OneClassSvmのscore関数のコメントアウトを外せば下記のようにちゃんと動作します。

$ python sampleAbsclass.py
echo echo hello hello
100 10

その他: abc.ABCMetaを使うときの注意点

他に、abc.ABCMetaを多用すると出てくる注意点として、クラスAが複数の抽象クラスを継承したとき、
「クラスAが1つでもabc.ABCMetaを継承した抽象クラスを有していれば、他の継承クラスもabc.ABCMetaを継承していないといけない」
という点があります。これを破ると以下のエラーメッセージが出ます。

TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases

これは他の言語でも同じ現象が起きるので、そうすることでの利便性があるのだと思います。プログラミング言語の理論をちゃんと学ぶべきですね...

その他2 abc.ABCMetaを使う例

scikit-learnで、abc.ABCMetaを利用している実装がありますのでそちらも見てみましょう。以下、_PLS - scikit-learnより引用します。__init__メソッドだけに@abstractmethodが追加されているのが分かります。

pls_.py
from abc import ABCMeta, abstractmethod
...
class _PLS(BaseEstimator, TransformerMixin, RegressorMixin, MultiOutputMixin,
           metaclass=ABCMeta):
...
    @abstractmethod
    def __init__(self, n_components=2, scale=True, deflation_mode="regression",
                 mode="A", algorithm="nipals", norm_y_weights=False,
                 max_iter=500, tol=1e-06, copy=True):
...
    def fit(self, X, Y):
...
    def transform(self, X, Y=None, copy=True):
...
    def predict(self, X, copy=True):
...
    def fit_transform(self, X, y=None):
...
    def _more_tags(self):
        return {'poor_score': True}

この_PLSクラスを他のクラスが継承するのですが、このときだけABCMetaが出てきています。_PLSクラス自体はMixinばかりを継承しており、1番目の例から、ABCMetaクラスを継承したものは持ちません。

なので、abc.ABCMetaの用途としては繰り返しになりますが複数クラスで必ず実装しないといけないメソッドがある場合に、抽象クラス内で継承するというものが挙げられるでしょう。

(TransformerMixinを継承しているのに_PLS内でfit_transformを再定義しているのは謎です。)

結論

本記事ではscikit-learnのTransformerMixinの実装例を出発点として、共通部分をまとめるためのクラスをどの様に作るのか、というのを簡単にまとめました。
abc.ABCMetaクラスを継承して抽象クラスとする、というのは常套手段ですが、単に1, 2個のメソッドを複数クラスに共通して持たせたい場合はabc.ABCMetaを使うまでもなさそうであることを確認しました。
同じ実装を何度も繰り返してしまうのは愚かなので、うまくクラス継承を利用しましょう。

参考文献

abc.ABCMeta を使う方法は以下の記事に紹介されています。

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3