Constants
A constant is a value that is meant to stay the same throughout a program’s execution — things like a mathematical constant, a configuration limit, or a fixed label. Python has no built-in mechanism to make a variable truly unchangeable; constants are entirely a matter of convention and, optionally, static-analysis tooling.
Python Has No True Constants
MAX_USERS = 100 print(MAX_USERS) # 100 MAX_USERS = 200 # Python allows this without complaint print(MAX_USERS) # 200 -- nothing enforced it was "constant"
The ALL_CAPS Convention
By convention (PEP 8), names intended to act as constants are written in ALL_CAPS_WITH_UNDERSCORES. This is purely visual — it signals intent to other developers reading the code, but Python itself treats these names exactly like any other variable.
PI = 3.14159
MAX_CONNECTIONS = 100
DEFAULT_TIMEOUT_SECONDS = 30
API_BASE_URL = "https://api.example.com"
def calculate_circle_area(radius):
return PI * radius ** 2Why Use Constants: Avoiding Magic Numbers
A “magic number” (or magic string) is a literal value scattered through code with no explanation of what it means. Replacing magic values with named constants makes code self-documenting and gives you a single place to change the value later.
# Hard to understand: what is 0.08? what is 3?
def total_cost(price, quantity):
return price * quantity * 1.08
# Clearer: the constant names explain themselves
TAX_RATE = 0.08
def total_cost(price, quantity):
return price * quantity * (1 + TAX_RATE)typing.Final: Enforcement via Static Checkers
Since Python 3.8, the typing module provides Final, an annotation that tells static type checkers (like mypy or your editor’s type checker) that a name should never be reassigned. It has zero effect at runtime — Python still won’t stop a reassignment when the program actually runs — but it lets tooling catch the mistake before you ship it.
from typing import Final MAX_RETRIES: Final = 3 API_VERSION: Final[str] = "v2" # A static checker (mypy) would flag the next line as an error, # even though Python itself would run it just fine: # MAX_RETRIES = 5 # error: Cannot assign to final name "MAX_RETRIES"
A More Robust Alternative: enum.Enum
When you have a group of related constants (say, the possible statuses of an order), Python’s enum module is a much more robust tool than a handful of loose ALL_CAPS variables. Enum members are distinct, comparable, and immutable by design.
from enum import Enum
class OrderStatus(Enum):
PENDING = "pending"
SHIPPED = "shipped"
DELIVERED = "delivered"
CANCELLED = "cancelled"
order_status = OrderStatus.SHIPPED
print(order_status) # OrderStatus.SHIPPED
print(order_status.name) # SHIPPED
print(order_status.value) # shipped
print(order_status == OrderStatus.SHIPPED) # True
for status in OrderStatus:
print(status.name, status.value)Compared to plain ALL_CAPS variables, enums group related values under one namespace, prevent accidental typos (OrderStatus.SHIPED is an AttributeError, not a silent bug), and make invalid values impossible to construct by accident.
Choosing an Approach
Approach | Enforcement | Best For |
|---|---|---|
ALL_CAPS variable | None — pure convention | A single, standalone constant (PI, MAX_RETRIES). |
typing.Final | Static-checker only, no runtime effect | Signalling intent when your team already runs mypy. |
enum.Enum | Structural — members are fixed, distinct objects | A closed group of related named values (statuses, roles, directions). |
Key Takeaways
Python has no true constants — any name can be reassigned at any time.
ALL_CAPS naming is the universal convention signaling "treat this as a constant."
Named constants replace magic numbers/strings, making code self-documenting.
typing.Finalgives static checkers (not the runtime) the ability to flag reassignment.enum.Enumis the most robust option for a fixed, related group of constant values.