Pythonでclassを使う: 設計編(2) 汎用親クラスの設計
結論
from abc import ABCMeta, abstractmethod
class BaseModel(metaclass=ABCMeta):
__slots__ = ('name')
@abstractmethod
def calc(self):
pass
class Model(BaseModel):
__slots__ = ('name')
def calc(self):
#具体的な処理を記述
pass
はじめに
似たようなclassを複数作成する場合の汎用的な親クラスの作成方法について述べます。
内容
汎化用のclassの要件
汎化用のclassに求める要件は以下です。
- 用いる共通メソッドを指定できる
- 用いるインスタンス変数を指定できる(もしくは用いるインスタンス変数を指定できる環境を提供する)
1については、汎用的なclassに必要な最低限のメソッドを定義できるので、親クラスさえ見ればそのクラスで何をやっているかわかるのでプログラムの可読性が上がる。
また以下のような関数で、(型ヒントを用いて)オブジェクトのメソッドを呼ぶ際にも(共通のメソッドがあるとわかっているので)役立ちます。
def call_model_calc(model:BaseModel):
model.calc()
abc
abc
を使う目的は、用いる共通メソッドを指定できることです。
使用方法は以下です。
metaclassでABCMeta
を継承します。
メソッドは、abstractmethod
でデコレートします。
from abc import ABCMeta, abstractmethod
class BaseModel(metaclass=ABCMeta):
@abstractmethod
def calc(self):
pass
似たようなことはtyping.Protocol
でもできますが、意図が異なるので今回はabc
を用います。
詳細については[2]の参考記事がわかりやすいです。
BaseModel
を継承したクラスは、BaseModel
のabstractmethod
を定義していないとTypeError
が返されます。
class WrongModel(BaseModel):
pass
In : w = WrongModel()
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-11-51ac0255938f> in <module>
----> 1 w = WrongModel()
TypeError: Can't instantiate abstract class WrongModel with abstract methods calc
__slots__
__slots__
を使う目的は、用いるインスタンス変数を指定できる(もしくは用いるインスタンス変数を指定できる環境を提供する) ことです。
__slots__
で定義した変数以外のインスタンス変数は使えなくなるので、インスタンス変数を制限したいときに役立ちます。(また使用メモリも節約できます)
実行例は以下で、__slots__
で定義していない変数を使うとAttributeError
が返されます。
In : m = Model()
In : m.name = 'name' #errorが出ない
In : m.hoge = 1
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-7-5f285424b34a> in <module>
----> 1 m.hoge = 1
AttributeError: 'Model' object has no attribute 'hoge'
親クラスで定義した__slots__
は子クラスで上書きできます。
このとき親クラスで__slots__
を定義しておかないと、子クラスで__slots__
を使うことができない仕様です[1]。(なので親クラスで__slots__
を使用する目的に括弧書きで用いるインスタンス変数を指定できる環境を提供すると記載しました。)
具体例
例えば機械学習モデルの親クラスと子クラスは以下のように定義すればよいです。
from abc import ABCMeta, abstractmethod
class BaseMLModel(metaclass=ABCMeta):
__slots__ = ('parameters')
def __init__(self, parmeters):
self.paramters = parameters
# basefunc
def get_parameters(self):
return self.parameters
@abstractmethod
def forward(self):
pass
class Model(BaseMLModel):
__slots__ = ('parameters')
def forward(self):
#具体的な処理を記述
pass
過去記事
参考
[1] __slots__
について
[2] abc
とProtocol
について
Discussion