Pythonでは定数は存在しないようだ。見通しの良い名前空間にすることを意識して、定数を簡易に実装する方法を検討した。
開発環境:
Python 3.9.2
PEP8より
定数は通常モジュールレベルで定義します。全ての定数は大文字で書き、単語をアンダースコアで区切ります。例として MAX_OVERFLOW や TOTAL があります。
Pythonのコーディング規約であるPEP8 には上記のように定数について言及されている。あくまでコーディング規約であるため再代入可能だ。
>>> MY_CONST = 31
>>> print(MY_CONST)
31
>>> MY_CONST = 9
>>> print(MY_CONST)
9
メタクラスを用いて定数を実装しよう。
>>> # 定数用メタクラス
>>> class MetaConstant(type):
... def __setattr__(self, name, value):
... if name in self.__dict__:
... raise TypeError(f'Can\'t redefine constant ({name})')
... else:
... self.__setattr__(name, value)
このメタクラスは以下のように使用する。
>>> # 定数クラス
>>> class MyConst(metaclass=MetaConstant):
... COUNT = 10
...
>>> for i in range(MyConst.COUNT):
... print(i)
0
1
2
3
4
5
6
7
8
9
>>> MyConst.COUNT = 2 #再代入すると・・・
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in __setattr__
TypeError: Can't redefine constant (COUNT)
>>> class MySub(MyConst):
... def __init__(self):
... self.COUNT = 7
... def get_count(self):
... return self.COUNT + MyConst.COUNT
... def do_overwrite(self):
... MyConst.COUNT = 65
... def do_nothing(self):
... return MyConst.COUNT
...
>>> test = MySub()
>>> test.get_count() # 同名インスタンス変数と定数の加算
17
>>> test.do_overwrite() # 再代入メソッドを実行すると・・・
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 7, in do_overwrite
File "<stdin>", line 4, in __setattr__
TypeError: Can't redefine constant (COUNT)
>>> test.do_nothing()
10
>>> id(MyConst.COUNT) # idを確認すると・・・
4356737616
>>> id(test.COUNT) # 当然ながらインスタンス変数の参照先は異なる
4356737520
>>> id(test.do_nothing()) # これはidが一致する
4356737616
>>>
簡易的な実装のため下記のようにインスタンス化した後はインスタンス変数に対して代入出来る。この結果、インスタンス変数名でクラス変数を参照出来なくなる。しかし定数としての厳密さを追求していないため満足している。
>>> class MySub2(MyConst):
... pass
...
>>> test = MySub2() # インスタンス化すると・・・
>>> print(test.COUNT)
10
>>> id(test.COUNT) # idを確認
4356737616
>>> test.COUNT = 1 # インスタンス変数が定義されてtest.COUNTではクラス変数を参照出来なくなった
>>> print(test.COUNT)
1
>>> id(test.COUNT) # インスタンス変数は参照先も異なる
4356737328
まとめ
Pythonに定数は存在しないが、簡易的な実装でそれらしく出来る