やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

メタクラスを継承して__setattr__による再代入禁止とsys.modulesへのクラスインスタンス代入を共通化した

これでPythonで定数を実装するクラスを手軽につくれそう。

成果物

GitHubPython.Const.201709141359

前回まで

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に定数っぽいものを実装する方法、だと思う。確証はない。