r0w0

PythonやDeepLearning関連で学んだこと、調べたことの備忘録

PythonのAbstractクラスの実装

状況

Pythonのクラス継承をよく使うものの、特に抽象クラスの実装をサブクラスに強制させないような実装をしていた(他人が読むコードではないので)。 実際どうやるんだろうと調べたのでサンプルコード付きで残しておく。

ポイント

  1. 抽象クラスでmetaclass引数に対してABCMetaを与えて継承
  2. 抽象化する関数に @abc.abstractmethod のデコレータをつける

Pythonのドキュメント

@abc.abstractmethodに記載

このデコレータを使うには、クラスのメタクラスが ABCMeta かそれを継承したものである必要があります。

サンプルコード

サブクラスで抽象化メソッドを実装せずエラーが出力される例

from abc import abstractmethod
from abc import ABCMeta


class Abstract(metaclass=ABCMeta):
    def __init__(self):
        super().__init__()
        pass

    @abstractmethod
    def show_message(self):
        pass


class SubClass(Abstract):
    def __init__(self):
        super().__init__()


sub = SubClass()
sub.show_message()

# TypeError: Can't instantiate abstract class SubClass with abstract methods show_message

サブクラスで抽象化メソッドを実装しているのでエラーは出力されない例

from abc import abstractmethod
from abc import ABCMeta


class Abstract(metaclass=ABCMeta):
    def __init__(self):
        super().__init__()
        pass

    @abstractmethod
    def show_message(self):
        pass


class SubClass(Abstract):
    def __init__(self):
        super().__init__()

    def show_message(self):
        print("hoge hoge")


sub = SubClass()
sub.show_message()

# hoge hoge
# Process finished with exit code 0

抽象化メソッドを指定するデコレータをつけていないのでエラーが出力されない例

from abc import abstractmethod
from abc import ABCMeta


class Abstract(metaclass=ABCMeta):
    def __init__(self):
        super().__init__()
        pass

    def show_message(self):
        pass


class SubClass(Abstract):
    def __init__(self):
        super().__init__()


sub = SubClass()
sub.show_message()

# Process finished with exit code 0

一瞬はまった点

最初、普通にABCMetaクラスを継承させたら怒られました。

class Abstract(ABCMeta)

みたいな。

こちらの記事が参考になったのですが、metaclassに指定したクラスはインスタンス生成時の挙動(継承メソッドの実装有無など)の規定・チェックに用いられるみたいですね。

ABCMetaはabstractmethodのデコレータが付与されたメソッドが、サブクラスの作成しようとしているインスタンスに含まれているか確認してくれてるのだと思います。