Constants in Python
Constants by convention, typing.Final, and enum
Constants by Convention
Python does not have a built-in mechanism for declaring true constants — there is no const keyword that prevents reassignment. Instead, Python relies on a naming convention: variables written in SCREAMING_SNAKE_CASE are understood by the community to be constants that should not be modified. This convention is documented in PEP 8, the official Python style guide.
# Module-level constants (by convention)
MAX_RETRIES = 3
DEFAULT_TIMEOUT = 30.0
API_BASE_URL = "https://api.example.com/v1"
SUPPORTED_FORMATS = ("png", "jpg", "webp") # tuple for immutabilitySince Python constants are enforced only by convention, nothing prevents a developer from reassigning MAX_RETRIES = 999 at runtime. This is a deliberate design choice in Python — the language trusts developers to respect conventions. Linting tools like Pylint and Ruff can flag reassignment of uppercase variables to catch accidental mutations.
typing.Final (Python 3.8+)
Python 3.8 introduced typing.Final, which signals to type checkers that a variable should not be reassigned. Mypy, Pyright, and other type checkers enforce this at analysis time, though it has no runtime effect.
from typing import Final
MAX_CONNECTIONS: Final = 100
API_VERSION: Final[str] = "v2"
FEATURE_FLAGS: Final[dict[str, bool]] = {"new_ui": True, "beta_api": False}
# MAX_CONNECTIONS = 200 # Mypy error: Cannot assign to final nameFinal works with both module-level variables and class attributes. When used in a class, it prevents subclasses from overriding the attribute. This makes it particularly useful for configuration values in class-based settings.
Enum Constants
Python's enum module (standard library since Python 3.4) provides proper constant enumerations with value safety, iteration, and membership testing. Enum members are singleton instances that cannot be reassigned or duplicated.
from enum import Enum, IntEnum, auto
class Color(Enum):
RED = "red"
GREEN = "green"
BLUE = "blue"
class HttpStatus(IntEnum):
OK = 200
NOT_FOUND = 404
SERVER_ERROR = 500
class Priority(Enum):
LOW = auto()
MEDIUM = auto()
HIGH = auto()
# Usage
status = HttpStatus.OK
print(status.value) # 200
print(status.name) # "OK"
# Exhaustive matching with match/case (Python 3.10+)
match status:
case HttpStatus.OK:
print("Success")
case HttpStatus.NOT_FOUND:
print("Not found")For numeric constants that need to participate in arithmetic, use IntEnum or IntFlag. For string constants, use StrEnum (Python 3.11+). For constants that should never be compared with plain integers or strings, use plain Enum.
Built-in Constants
Python provides several built-in constants and many more through the standard library. The math module provides mathematical constants, sys exposes interpreter limits, and modules like string provide useful character sets.
import math
import sys
import string
# Math constants
math.pi # 3.141592653589793
math.e # 2.718281828459045
math.tau # 6.283185307179586 (2 * pi)
math.inf # Positive infinity
math.nan # Not a Number
# System constants
sys.maxsize # Largest possible list index (2^63 - 1 on 64-bit)
sys.float_info # Named tuple with float limits
sys.version_info # Python version as named tuple
# String constants
string.ascii_lowercase # 'abcdefghijklmnopqrstuvwxyz'
string.digits # '0123456789'
string.punctuation # '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'
# Built-in constants
True, False # Boolean constants
None # Null/nil equivalent
... # Ellipsis (used in type hints and slicing)
__debug__ # True unless running with -O flagImmutable Data Structures
When defining constants that are collections, prefer immutable types. Use tuples instead of lists, frozensets instead of sets, and consider types.MappingProxyType for read-only dictionary views:
from types import MappingProxyType
# Immutable sequence
ALLOWED_EXTENSIONS = ("py", "pyi", "pyx")
# Immutable set
RESERVED_WORDS = frozenset({"if", "else", "while", "for", "def", "class"})
# Read-only dict view
_CONFIG = {"debug": False, "log_level": "info"}
CONFIG = MappingProxyType(_CONFIG)
# CONFIG["debug"] = True # TypeError: does not support item assignmentCommon Patterns
- Define module-level constants at the top of the file, after imports and before class or function definitions.
- Use
typing.Finalfor type-checked immutability andSCREAMING_SNAKE_CASEfor visual convention — both together provide the strongest signal. - Prefer tuples over lists and frozensets over sets for constant collections, since they are hashable and inherently immutable.
- Use
dataclasses.dataclass(frozen=True)orNamedTuplefor constant records with named fields.