Skip to content

How to Create Constants in Python

Learn how to define and enforce constants in Python using naming conventions, typing.Final, namedtuple, frozen dataclasses, and Enum for immutability and clean code.

Unlike languages such as Java or C++, Python does not have a built-in const keyword to declare constants. However, constants are a fundamental part of writing clean, maintainable code: they communicate that a value should never change after it's defined.

This guide explores the conventional approach to defining constants in Python and several techniques that enforce immutability to varying degrees, helping you choose the right strategy for your project.

The Convention: UPPER_CASE Variable Names

The most common and widely recognized way to define constants in Python is by using ALL_UPPERCASE names with underscores. This is a naming convention documented in PEP 8, Python's official style guide.

PI = 3.14159
GRAVITY = 9.8
SPEED_OF_LIGHT = 299792458
MAX_CONNECTIONS = 100

print(PI)
print(SPEED_OF_LIGHT)

Output:

3.14159
299792458

While the naming convention is sufficient for most projects, there are several techniques that provide stronger guarantees of immutability.

Using typing.Final (Python 3.8+)

Starting with Python 3.8, you can annotate a variable with Final from the typing module. This tells static type checkers like mypy that the variable should not be reassigned.

from typing import Final

PI: Final = 3.14159
GRAVITY: Final = 9.8
SPEED_OF_LIGHT: Final = 299792458

print(f"PI: {PI}")
print(f"Gravity: {GRAVITY}")

Output:

PI: 3.14159
Gravity: 9.8

What Happens If You Reassign?

At runtime, Python will not raise an error: Final is a hint, not a runtime enforcement.

from typing import Final

PI: Final = 3.14159
PI = 3.14  # No runtime error

print(PI)

Output:

3.14

However, running mypy on this code will catch the issue:

$ mypy script.py
script.py:4: error: Cannot assign to final name "PI"

Using namedtuple for Grouped Constants

A namedtuple creates a lightweight, immutable object. This is an excellent choice when you have a set of related constants that belong together logically.

from collections import namedtuple

Constants = namedtuple("Constants", ["pi", "gravity", "speed_of_light"])

PHYSICS = Constants(pi=3.14159, gravity=9.8, speed_of_light=299792458)

print(f"PI: {PHYSICS.pi}")
print(f"Gravity: {PHYSICS.gravity}")
print(f"Speed of Light: {PHYSICS.speed_of_light}")

Output:

PI: 3.14159
Gravity: 9.8
Speed of Light: 299792458

Attempting to Modify a Value

Because namedtuple instances are immutable, trying to change an attribute raises an AttributeError:

PHYSICS.pi = 3.14  # ❌ Raises AttributeError

Output:

AttributeError: can't set attribute

This provides true runtime immutability, making namedtuple one of the strongest options for constants in Python.

Using @dataclass(frozen=True) for Immutable Constants

Python 3.7 introduced dataclasses. Setting frozen=True makes instances immutable, any attempt to modify an attribute raises a FrozenInstanceError.

from dataclasses import dataclass

@dataclass(frozen=True)
class PhysicsConstants:
    pi: float = 3.14159
    gravity: float = 9.8
    speed_of_light: int = 299792458

PHYSICS = PhysicsConstants()

print(f"PI: {PHYSICS.pi}")
print(f"Gravity: {PHYSICS.gravity}")
print(f"Speed of Light: {PHYSICS.speed_of_light}")

Output:

PI: 3.14159
Gravity: 9.8
Speed of Light: 299792458

Attempting to Modify a Value

PHYSICS.pi = 3.14  # ❌ Raises FrozenInstanceError

Output:

dataclasses.FrozenInstanceError: cannot assign to field 'pi'

Using Enum for a Set of Named Constants

The enum module is perfect when your constants represent a fixed set of related values, such as status codes, directions, or configuration options.

from enum import Enum

class Color(Enum):
    RED = 1
    GREEN = 2
    BLUE = 3

class HttpStatus(Enum):
    OK = 200
    NOT_FOUND = 404
    SERVER_ERROR = 500

print(Color.RED)
print(Color.RED.value)
print(HttpStatus.NOT_FOUND.value)

Output:

Color.RED
1
404

Attempting to Modify a Value

Color.RED = 99  # ❌ Raises AttributeError

Output:

AttributeError: cannot reassign member 'RED'

Enums are inherently immutable and also support iteration, comparison, and serialization, making them a powerful choice for constant sets.

Using __slots__ and a Custom Class

For advanced use cases, you can create a class that blocks attribute assignment after initialization using __setattr__:

class Constants:
    __slots__ = ()

    PI = 3.14159
    GRAVITY = 9.8
    SPEED_OF_LIGHT = 299792458

CONST = Constants()

print(CONST.PI)
print(CONST.GRAVITY)

Output:

3.14159
9.8

Comparison of Approaches

Method Runtime Immutability Type Checker Support Python Version Best For
UPPER_CASE convention ❌ (convention only) ❌ All Simple projects, small teams
typing.Final ❌ (static only) ✅ 3.8+ Projects using mypy or Pyright
namedtuple ✅ ❌ All Grouped, related constants
dataclass(frozen=True) ✅ ✅ 3.7+ Typed, structured constants
Enum ✅ ✅ 3.4+ Fixed sets of named values

Best Practices

  1. Always use UPPER_CASE names: regardless of which technique you choose, the naming convention makes intent clear at a glance.
  2. Store constants in a dedicated module: create a constants.py or config.py file and import values where needed:
    PI = 3.14159
    MAX_RETRIES = 5
    from constants import PI, MAX_RETRIES
  3. Don't use magic numbers: replace unexplained literal values with named constants to improve readability.
  4. Choose the right level of enforcement: for solo or small-team projects, the naming convention is usually enough. For libraries or large codebases, consider Final, frozen dataclasses, or enums.

Conclusion

While Python doesn't enforce constants at the language level, you have multiple effective strategies to define and protect them:

  • UPPER_CASE naming is the universal convention and should always be used.
  • typing.Final adds static analysis protection without changing runtime behavior.
  • namedtuple and dataclass(frozen=True) provide true runtime immutability for grouped constants.
  • Enum is the best choice for fixed sets of named, related values.

By combining clear naming conventions with the right immutability technique, you can write Python code that is safer, more readable, and easier to maintain.

Navigation