抽象基底クラス ABC(abstract base class)

【 目次 】

抽象基底クラスはpythonにおける抽象クラスを作成するためのメタクラスである。
抽象クラスとは実装すべきメソッドやプロパティを抽象メソッドや抽象プロパティとして定義したインスタンス化できないクラスである。
そして、抽象クラスを継承したクラスでは抽象クラスで定義されている抽象メソッドやプ抽象プロパティを実装しない限りそのクラスも抽象クラスとなりインスタンス化できない。
抽象クラスはJavaやC#におけるインターフェースの役割を果たし、抽象クラスを継承したクラスでは必要なメソッドの実装が保障されることになる。

メタクラスを使って抽象クラスを実装する

抽象クラスを実装するには、クラスのメタクラスにabcモジュールの抽象基底クラスABCMetaクラスを指定する必要がある。

しかし、メタクラスにABCMetaクラスを指定しただけではインスタンスの作成時にエラーは発生しないようである。

import abc

class AbsMyCls(object):
    __metaclass__ = abc.ABCMeta
    def method(self):
        print u"抽象クラスのメソッドなのにメソッドが実行できました。"

my_inst=AbsMyCls()
my_inst.method()

実行結果

抽象クラスのメソッドなのにメソッドが実行できました。

インスタンスの作成時にエラーは発生させるためには、1つ以上の抽象メソッドか抽象プロパティを指定する必要がある。

抽象メソッド

抽象メソッドの定義はデコレータ@abc.abstractmethodをメソッドに指定する事で定義する。

import abc

class AbsMyCls(object):
    __metaclass__ = abc.ABCMeta

    @abc.abstractmethod
    def abc_method(self):
        pass

抽象メソッドが定義されていると

my_inst=AbsMyCls()

抽象クラスはインスタンスが作成できないのでエラーが発生。

実行結果

TypeError: Can't instantiate abstract class MyCls with abstract methods abc_property

抽象クラスを継承したクラス(具象クラス)を作成してabc_methodメソッドを実装。

class MyCls(AbsMyCls):

    def abc_method(self):
        print u"abc_methodを実行"

my_inst=MyCls()
my_inst.abc_method()

具象クラスにてメソッドを実装したので実行可能に。

実行結果

abc_methodを実行

抽象プロパティ

抽象クラスには抽象プロパティを定義する事もできる。
抽象プロパティを定義するにはabcモジュールのabstractproperty関数もしくはデコーレータ@abstractpropertyを使う。

abstractproperty関数の例

import abc

class AbsMyCls(object):
    __metaclass__ = abc.ABCMeta

    def get_abc_property(self):
        pass

    def set_abc_property(self,value):
        pass

    abc_property = abc.abstractproperty(get_abc_property, set_abc_property)

class MyCls(AbsMyCls):
    def __init__(self):
        self._abc_property="default value"

    def get_abc_property(self):
        return self._abc_property

    def set_abc_property(self,value):
        self._abc_property=value

    abc_property = property(get_abc_property, set_abc_property)

my_inst=MyCls()
my_inst.abc_property="changed value"

print my_inst.abc_property

実行結果

changed value


デコーレータの例

@abstractpropertyを使って読取り専用のプロパティだけを定義。

import abc

class AbsMyCls(object):
    __metaclass__ = abc.ABCMeta

    @abc.abstractproperty
    def abc_property(self):
        pass

class MyCls(AbsMyCls):

    @property
    def abc_property(self):
        return u"プロパティ値"

my_inst=MyCls()
print my_inst.abc_property

実行結果

プロパティ値

抽象クラスで定義した抽象プロパティを具象クラスで実装する場合にはあらためて@propertyなどの再指定が必要になるので注意。

具象クラスから抽象クラスの抽象メソッドを呼び出す

抽象クラスにてもととなる抽象メソッドをあらかじめ実装しておいて具象クラスでそれをそれを呼び出す事もできる。

import abc

class AbsMyCls(object):
    __metaclass__ = abc.ABCMeta

    @abc.abstractmethod
    def abc_method(self):
        print u"abc_methodを実行"

class MyCls(AbsMyCls):

    def abc_method(self):
        super(MyCls, self).abc_method()

my_inst=MyCls()
my_inst.abc_method()

実行結果

abc_methodを実行

クラスの定義後、後から動的に抽象メソッドを追加する事はできない

公式ドキュメントには 「クラスに動的に抽象メソッドを追加する、あるいはメソッドやクラスが作られた後から抽象的かどうかの状態を変更しようと試みることは、サポートされません。」とありabstractmethod関数を使って後から動的に抽象メソッドを追加する事はできないようだ。

import abc

class AbsMyCls(object):
    __metaclass__ = abc.ABCMeta

def func(self):
    print u"methodを実行"

AbsMyCls.method=abc.abstractmethod(func)

class MyCls(AbsMyCls):
    pass

my_inst=MyCls()
my_inst.method()

MyClsは抽象メソッドmethodを実装していないにもかかわらずエラーにならない。

実行結果

methodを実行

抽象クラスより具象クラスを実装するもう1つの方法 - 具象クラスを抽象クラスに登録

具象クラスを実装するもう1つの方法として、抽象クラスを継承して具象クラスを実装するのではなく、具象クラスを抽象クラスに登録する方法がある。

class MyCls(object):

    def abc_method(self):
        print u"methodを実行"

AbsMyCls.register(MyCls)

registerメソッドを実行すると、具象クラスが抽象クラスを継承していなくとも、組込み関数issubclassやisinstanceは継承していると認識してTrueを返す。

print issubclass(MyCls, AbsMyCls)
print isinstance(my_inst, AbsMyCls)

実行結果

True
True

具象クラスが抽象メソッドを実装していなくとも、registerメソッドではエラーは発生しない。 そして、メソッドの実行時に初めてエラーが発生する事になる。

class MyCls(object):
    pass

AbsMyCls.register(MyCls)

my_inst=MyCls()
my_inst.abc_method()

実行結果

Traceback (most recent call last):
  File "xxx.py", line xxx, in <module>
    my_inst.abc_method()
AttributeError: 'MyCls' object has no attribute 'abc_method'

参考記事

ページのトップへ戻る