これでPythonで定数を実装するクラスを手軽につくれそう。
成果物
前回まで
- ClassPropertyを他のclassのclassmethodから参照するとプロパティとして見てくれない
- Pythonでreadonlyにする方法。
- sys.modules[key]にclassインスタンスを代入して複数回それをimportしてもインスタンス初期化されないことを確認した
- 再代入禁止クラスを独立させ、継承してみた
sys.modules
に__setattr__
で再代入禁止を実装したクラスのインスタンスを代入するという荒業。
今回の本題
メタクラスを使う
ConstMeta.py
class ConstMeta(type): class ConstError(TypeError): pass def __init__(self, name, bases, dict): print('__init__', name, bases, dict, self, type(self)) super(ConstMeta, self).__init__(name, bases, dict) import sys sys.modules[name]=self()#ConstMetaを継承したクラスのモジュールに、そのクラスのインスタンスを代入する def __setattr__(self, name, value): if name in self.__dict__.keys(): raise self.ConstError('readonly。再代入禁止。') super(ConstMeta, self).__setattr__(name, value)
MyConst.py
from ConstMeta import ConstMeta class MyConst(metaclass=ConstMeta): pass
0.py
import MyConst MyConst.test = 'test' print(MyConst.test) MyConst.test = 'test' #Const.ConstError: readonly。再代入禁止。
$ python 0.py test Const.ConstError: readonly。再代入禁止。
できた。
metaclassでないとダメ
継承するときのclass ... (metaclass=ConstMeta)
が冗長。class ... (ConstMeta)
にしたい。しかし、メタクラスでないとダメ。
metaclassはsys.modules
への代入が自動実行される。しかし普通のクラスだと明示的に親メソッド呼出しないと実行されない。
メタクラスならclass ... (metaclass=ConstMeta)
としたときにメタクラス内の定義が実行される、と思う。でも、ふつうのclassだと明示的に呼び出さないかぎり実行されない。継承したクラスで毎回、親メソッドを呼出してしまっては面倒さを回避できず意味がなくなる。
よって、metaclassで実装する必要がある。
所感
これがPythonで最もDRYに定数っぽいものを実装する方法、だと思う。確証はない。