Skip to content

prefer-module-level-constant

Hoist repeatedly read static values when a module constant preserves behavior.

Why

Rebuilding a substantial static collection repeats allocation, while calling re.compile with a constant pattern repeats a regex-cache lookup. Native immutable collections or Final dictionary bindings make the static lifetime explicit for proven read-only use.

Fix

Define the value once at module scope as a tuple, frozenset, Final[dict[K, V]], or compiled pattern, then reference it from the function. Final prevents reassignment in type-checked code but does not freeze dictionary contents; use MappingProxyType when runtime write protection is required. Preserve ordering and concrete-type behavior used by callers.

Examples

Before — flagged Static lookup dictionary rebuilt per call
service.py
def handle(key):
labels = {"a": 1, "b": 2, "c": 3, "d": 4, "e": 5, "f": 6, "g": 7, "h": 8}
return labels.get(key)
After — preferred Final lookup dictionary defined once
service.py
from typing import Final
LABELS: Final[dict[str, int]] = {
"a": 1,
"b": 2,
"c": 3,
"d": 4,
"e": 5,
"f": 6,
"g": 7,
"h": 8,
}
def handle(key):
return LABELS.get(key)
Before — flagged Substantial static membership table rebuilt per call
service.py
def handle(value):
allowed = ["a", "b", "c", "d", "e", "f", "g", "h"]
return value in allowed
After — preferred Immutable membership table defined once
service.py
ALLOWED = ("a", "b", "c", "d", "e", "f", "g", "h")
def handle(value):
return value in ALLOWED