pythonで考えるメタクラスとは
メタクラスについて、pythonで初めて学んでみました。自分の備忘録も兼ねて、まとめてみたいと思います。
そもそもメタクラスって?
メタクラスって何でしょう、Wikipediaで調べてみると下記のように説明されています。
オブジェクト指向プログラミングにおいてメタクラスとは、インスタンスがクラスとなるクラスのことである。通常のクラスがそのインスタンスの振る舞いを定義するように、メタクラスはそのインスタンスであるクラスを、そして更にそのクラスのインスタンスの振る舞いを定義する。
(引用:https://ja.wikipedia.org/wiki/%E3%83%A1%E3%82%BF%E3%82%AF%E3%83%A9%E3%82%B9)
つまり、__クラスの振る舞いを定義するためのクラス__がメタクラスだと、なんとなくイメージできます。
ここでクラスの「振る舞い」を制御したいのなら、そのクラスの親クラスを作って、継承させるのと何が違うのでしょうか。それは制御したい「振る舞い」に違いがあると思っています。
親クラス(継承)とメタクラスの違い
親クラスの継承とメタクラスの役割について、私なりにざっくり整理すると以下のようになると思っています。
制御したい「振る舞い」 | 具体例 | |
---|---|---|
親クラス | インスタンス__生成後__の挙動(どのように動くか) | 複数の子クラスで同じ名前のメソッドを使いたい(ポリモーフィズムの実現) |
メタクラス | インスタンス__生成前(生成時)__の挙動(どのように定義されているか) | クラス変数がすべて適切に定義されているかチェックしたい/直接関係しないクラス同士の中身も制御したい。 |
コード例
実際にコード例を見てみましょう。政令指定都市を定義するためのメタクラスを書いてみました。政令指定都市は人口が500万人以上の都市ですので、政令指定都市クラス定義時に人口が500万人以上かをチェックするようにします。
# 政令指定都市メタクラス
# メタクラスはtypeを継承する
class GovermentDesignatedMetaClass(type):
def __new__(meta,name,bases,attributes):
# __new__関数はクラス生成時に実行される関数
# クラスの情報を受け取ることができる
# name:クラス名, bases:親クラス, attributes:クラス属性
if bases != (object,): #抽象クラスは検証しない
if attributes["population"] < 5000000: # 500万人未満なら例外
raise ValueError("This is not Goverment designated city")
return type.__new__(meta,name,bases,attributes)
# 政令指定都市親クラス
# メタクラスの指定は「metaclass=...」で行う(Python3)
class GovermentDesignatedCity(object, metaclass=GovermentDesignatedMetaClass):
population=None #子クラスで定義する
# 東京都 (政令指定都市)
class Tokyo(GovermentDesignatedCity):
population = 10000000 #1千万人
# 松山市 (政令指定都市ではない)
class Matsuyama(GovermentDesignatedCity):
population = 500000 #50万人
以上のようにメタクラスを定義し実行すると、Matsuyamaは人口が約50万人なので政令指定都市ではないため、下記のようにエラーメッセージが出力されます。
Traceback (most recent call last):
File "sample.py", line 15, in <module>
class Matsuyama(GovermentDesignatedCity):
File "sample.py", line 5, in __new__
raise ValueError("This is not Goverment designated city")
ValueError: This is not Goverment designated city
メタクラスのメリット
お気づきかと思いますが、上記コードはMatsuyamaクラスを定義しただけで、どこにもインスタンスを生成していませんし、静的クラスとしても利用していません。これこそがメタクラスのメリットの一つになります。すなわち、
メタクラスを利用することで、クラスを実装した時点でそのクラスの定義チェックを行えます
メタクラスの使いどころ
最後にメタクラスの使いどころになると考えられているシチュエーションを挙げます。
(他にも積極的に使える場面があれば、ぜひご教示ください。)
- クラス変数のチェックをしたい(今回のコード例)
- インスタンス生成時に必ず特定の関数を実行したい。(デシリアライズ時にジェネリックに行えるよう、シリアライズ時にクラスの存在を登録したいときなど)
まとめ
メタクラスはクラスがどのように定義されるかを制御でき、クラス定義時のクラス変数チェックなどに有効。
参考記事
Python の メタプログラミング (__metaclass__, メタクラス) を理解する
Python超入門その24〜メタクラスでクラスの動作をカスタマイズしてみよう〜
Brett Slatkin, "Effective Python", 2016, O'REILLY