pythonには、他の言語のようなconstキーワードはなく、言語レベルで定数を保護する機構はありません。
ただ、定数にあたる値が変更されることの内容に保護する仕組みを工夫することはできます。
命名規約による暗黙の定数保護
PEP 8 では、定数はすべて大文字(UPPER_CASE)+アンダースコア区切りで記述することが推奨されています。
Python
MAX_SIZE = 100
DEFAULT_TIMEOUT = 30
PI = 3.14159
基本的にはこの命名規約が定数的意図を伝える唯一の公式手段となります。
規律のあるチームであればこれで十分な気もしますが、他にも色々な方法があります。
型ヒントを使う
型ヒントを使って、その変数が再代入できないことを明示できます。型ヒントですので実行時の制限はできませんが、mypy等静的型チェッカーで問題を検出できます。(PEP591)
Python
from typing import Final
PI: Final[float] = 3.14159
PI = 3 # 型チェッカーがエラーを出す
enumを使う
用途が限定的で必ずしも定数の代わりになるわけではありませんが、enumを定数的に使うことも可能です。
Python
from enum import Enum
class Color(Enum):
RED = 1
GREEN = 2
BLUE = 3
# 使用例
current = Color.RED
不変オブジェクト(frozen=True)
frozen=Trueで不変オブジェクトにしてしまえば、属性の設定をすべてまとめて変更不可(イミュータブル)にすることができます。
Python
from dataclasses import dataclass
@dataclass(frozen=True)
class Config:
host: str
port: int
cfg = Config(host="https://example.com", port=8080)
# cfg.port = 9090 # frozen=Trueのため実行時に属性変更で例外
カスタムクラスで属性変更の禁止
やや大げさで個人的にはpythonらしくない、と感じてしまうのですが、カスタムクラスを定義して属性を強引に保護する方法もあります。
Python
class ConstNamespace:
def __init__(self, **kwargs):
object.__setattr__(self, '_locked', False)
for k, v in kwargs.items():
setattr(self, k, v)
object.__setattr__(self, '_locked', True)
def __setattr__(self, name, value):
if getattr(self, '_locked', False):
raise AttributeError(f"{name} is constant")
super().__setattr__(name, value)
cn = ConstNamespace(PI=3.14)
print(cn.PI) # 3.14
cn.PI = 3.14159 # AttributeError
内部に_lockedという保護フラグを設定します。初期化時に値を設定することを可能にし、以降は属性の変更を禁止してしまいます。結果として、初期化後に値の変更は一切できなくなるわけです。
コメント