2021-03-18追記
さすがに適当に書きすぎてたので、もうちょっとちゃんと書き直しました。
下記のコードは継承周りでバグがあったので、**ここ**にあるコードを使ってください。
TL;DR
- [1] Constantクラスを定義する(下記のコードコピペ).
class ConstantError(Exception):
"""Constantクラスの例外"""
pass
class MetaConstant(type):
"""Constantクラスのメタクラス"""
# __init__関数置き換え用の関数
def _meta__init__(self, *args, **kwargs):
raise ConstantError("Can't make instance of Constant class")
def __new__(cls, classname, bases, dict):
# 定数クラスのインスタンス生成を禁止
dict["__init__"] = cls._meta__init__
# 継承時の定数再定義を禁止
const_names = [name for name in dict if not name.startswith("__")]
base_consts = set(*[base.__dict__ for base in bases])
for name in const_names:
if name in base_consts:
raise ConstantError(f"Can't rebind constant [{name}]")
return type.__new__(cls, classname, bases, dict)
def __setattr__(self, name, value):
if name.startswith("__"):
super(MetaConstant, self).__setattr__(name, value)
else:
raise ConstantError(f"Can't set attribute to Constant [{name}]")
class Constant(metaclass=MetaConstant):
"""定数クラス"""
pass
- [2] Constantを継承したクラスを宣言する。
class MyConstant(Constant):
"""サンプルの定数クラス
Attributes
----------
FOO : int
methods
-------
twice_of_FOO
"""
# クラス変数として定数を定義する
FOO: int = 1
# クラスメソッドも定義できる
@classmethod
def twice_of_FOO(cls):
"""FOOの二倍を返す関数
Returns
-------
int : 2*MyConstant.FOO
"""
return 2*cls.FOO
# 定義した定数クラスのサブクラスも作れる
# (サブクラスから親クラスの定数も参照できる)
class MySubConstant(MyConstant):
"""サンプルの定数クラス
Attributes
----------
FOO : int (by MyConstant)
BAR : int
"""
BAR: int = 2
# 参照は通常のクラス変数と同じように参照する
print(f"MyConstant.FOO = {MyConstant.FOO}")
print(f"MyConstant.twice_of_FOO() = {MyConstant.twice_of_FOO()}")
print(f"MySubConstant.FOO = {MySubConstant.FOO}")
print(f"MySubConstant.BAR = {MySubConstant.BAR}")
# 代入はできない
MyConstant.FOO = 2 # ConstantError
# あとから追加も不可
MyConstant.BAZ = 3 # ConstantError
# ただし例外的に"__"(アンダースコア2個)から始まるものは代入・変更OK
# 用法用量を守って正しく使ってね
MyConstant.__BAZ = 3
print(f"MyConstant.__BAZ = {MyConstant.__BAZ}")
# インスタンスの生成ができない
constant_instance = MyConstant() # ConstantError
モチベーション
こことかのコードは使えるは使えるんだが、あとから定数を付け足せてしまったり、参照可能な定数を切り分けられない辺りにもんやりしてしまっていた。
ので、n番煎じではありますが、定数クラス作ってみることにしました。
仕様
とりあえず、仕様としては下記のとおりとした。
- クラス宣言時のみ、定数の宣言ができる(あとから付け足し不可)
- 定数クラスのインスタンス生成は禁止
- 継承関係を持たせることで、参照範囲を切り分け可
で
出来たのが上記のコードですが、正直ここまで書いといて「(いや絶対探せばどっかにあるやん)」ってなってます。
これよりエレガントなコードあったら教えてくださいお願いします。
ちなみに
見る人が見ればわかると思いますが、ミュータブルなデータ(辞書型とか)は、再代入不可でも操作はできるので、中身は変わったりします。
なので、イミュータブルなデータのみにするか、イミュータブルな辞書を使ったりしましょう。