NeuroAgent

How to Create Constants in Python: Complete Guide

Learn how to create constants in Python using naming conventions, typing.Final, and other methods. Complete guide with examples comparing to Java constants and best practices.

Question

How do I create a constant in Python?

How do I declare a constant in Python?

In Java, we do:

java
public static final String CONST_NAME = "Name";
NeuroAgent

Python doesn’t have a built-in constant type like Java, but you can create constants by using naming conventions and language features. The standard approach is to use uppercase letters with underscores for constant names, and while Python doesn’t enforce immutability, developers typically respect these conventions by not modifying variables declared as constants.

Contents

Understanding Python’s Approach to Constants

Python doesn’t have a const keyword or built-in constant mechanism like Java, C++, or other statically typed languages. Instead, Python relies on convention over configuration. The language philosophy emphasizes programmer responsibility rather than language enforcement.

python
# This is how you would typically declare a constant in Python
MAX_CONNECTIONS = 100
API_BASE_URL = "https://api.example.com"
DEBUG_MODE = False

The Python Enhancement Proposal 8 (PEP 8) establishes naming conventions that signal to developers that certain values should be treated as constants. While you can technically modify these values, doing so violates the convention and can lead to unexpected behavior in production code.

“We are all consenting adults here” - This Python philosophy means the language trusts developers to follow conventions rather than enforcing rules through syntax.

Naming Conventions for Constants

Following PEP 8 guidelines, constants should be:

  • All uppercase letters: Use uppercase to distinguish constants from variables
  • Snake case: Separate words with underscores
  • Descriptive names: Choose names that clearly indicate the constant’s purpose
python
# Good constant naming
MAX_RETRIES = 3
DATABASE_URL = "postgresql://localhost:5432/mydb"
API_TIMEOUT = 30.0

# Avoid these patterns
maxRetries = 3  # Uses camelCase (should be for classes)
Max_Retries = 3  # Mixed case
max_retries = 3  # Lowercase (looks like a variable)

Special cases for constants:

  • Constants that are class-level should use uppercase with underscores
  • Constants that are module-level should also follow the same convention
  • Constants that are private (intended for internal use) should start with a single underscore
python
# Module-level constants
DEFAULT_PORT = 8080
CACHE_TTL = 3600

# Class-level constants
class DatabaseConnection:
    MAX_POOL_SIZE = 10
    CONNECTION_TIMEOUT = 30

# Private constants (internal use)
_INTERNAL_API_KEY = "abc123"

Different Ways to Create Constants

1. Simple Variable Assignment (Most Common)

The simplest approach is just using uppercase variable names:

python
PI = 3.14159
GRAVITY = 9.81
SPEED_OF_LIGHT = 299792458

2. Using typing.Final (Python 3.8+)

Python 3.8 introduced the Final type hint to indicate that a variable should not be reassigned:

python
from typing import Final

MAX_USERS: Final[int] = 1000
API_ENDPOINT: Final[str] = "https://api.example.com"

While Final doesn’t prevent reassignment at runtime, it provides:

  • Static type checking compatibility with tools like MyPy
  • Documentation of intent
  • Runtime protection in some contexts

3. Using Enums for Constants with Values

For related constants, consider using Enum:

python
from enum import Enum

class Status(Enum):
    ACTIVE = "active"
    INACTIVE = "inactive"
    PENDING = "pending"

# Usage
current_status = Status.ACTIVE

4. Using NamedTuple or Data Classes

For constants with multiple related values:

python
from typing import NamedTuple

class Config(NamedTuple):
    DATABASE_URL: str
    API_KEY: str
    TIMEOUT: int

config = Config(
    DATABASE_URL="postgresql://localhost:5432/mydb",
    API_KEY="abc123",
    TIMEOUT=30
)

5. Using Module-Level Constants

Create a dedicated constants module:

python
# constants.py
import os

# Application settings
DEBUG = os.getenv("DEBUG", "False").lower() == "true"
SECRET_KEY = os.getenv("SECRET_KEY", "default-secret-key")

# Database settings
DATABASE_URL = os.getenv("DATABASE_URL", "sqlite:///default.db")
MAX_CONNECTIONS = int(os.getenv("MAX_CONNECTIONS", "10"))

# API settings
API_VERSION = "v1"

Best Practices and Examples

Basic Constant Usage

python
# Configuration constants
DEFAULT_TIMEOUT = 30
MAX_RETRIES = 3
CACHE_SIZE = 1000

# Mathematical constants
PI = 3.14159
E = 2.71828
GOLDEN_RATIO = 1.61803

# Application constants
APP_NAME = "MyApp"
VERSION = "1.0.0"

Constants in Functions

python
def calculate_circle_area(radius):
    """Calculate the area of a circle using the PI constant."""
    return PI * radius ** 2

def fetch_with_retry(url, timeout=DEFAULT_TIMEOUT, retries=MAX_RETRIES):
    """Fetch data with retry logic."""
    # Implementation here
    pass

Constants in Classes

python
class DatabaseConnection:
    # Class constants
    MAX_POOL_SIZE = 10
    CONNECTION_TIMEOUT = 30
    DEFAULT_PORT = 5432
    
    def __init__(self, host, port=DEFAULT_PORT):
        self.host = host
        self.port = port
        # Connection logic here

Constants with Configuration

python
import os
from typing import Final

# Environment-based constants
DEBUG_MODE: Final[bool] = os.getenv("DEBUG", "False").lower() == "true"
API_BASE_URL: Final[str] = os.getenv("API_URL", "https://default.api.com")
DATABASE_URL: Final[str] = os.getenv("DATABASE_URL")

# Fallback constants
FALLBACK_DATABASE_URL = "sqlite:///fallback.db"

Comparison to Java Constants

While Java enforces constants at the language level, Python uses a different approach:

Java:

java
public static final String API_URL = "https://api.example.com";
public static final int MAX_USERS = 1000;
public static final boolean DEBUG_MODE = false;

Python:

python
API_URL = "https://api.example.com"
MAX_USERS = 1000
DEBUG_MODE = False

Key Differences:

Feature Java Python
Enforcement Language-level enforcement Convention-based
Type Safety Compile-time checking Runtime flexibility
Multiple Types static final for class-level Module-level by convention
Modification Impossible at runtime Possible but discouraged
Documentation Built into language Through naming conventions

When You Need True Constants

If you need runtime enforcement of constants, consider these approaches:

python
# Using property with read-only access
class Constants:
    _MAX_USERS = 1000
    
    @property
    def MAX_USERS(self):
        return self._MAX_USERS

# Using namedtuple (immutable)
from collections import namedtuple
Constants = namedtuple('Constants', ['MAX_USERS', 'API_URL'])
CONSTANTS = Constants(MAX_USERS=1000, API_URL="https://api.example.com")

# Using frozen dataclass (Python 3.7+)
from dataclasses import dataclass
@dataclass(frozen=True)
class Constants:
    MAX_USERS: int
    API_URL: str

Advanced Constant Techniques

Constants with Validation

python
from typing import Final

# Constants that include validation
PORT_RANGE = range(1, 65536)

def validate_port(port: int) -> None:
    if port not in PORT_RANGE:
        raise ValueError(f"Port must be between 1 and 65535, got {port}")

DEFAULT_PORT: Final[int] = 8080
validate_port(DEFAULT_PORT)

Dynamic Constants

python
# Constants that are computed at import time
import os
import platform

SYSTEM_NAME: Final[str] = platform.system()
IS_WINDOWS: Final[bool] = platform.system() == "Windows"
IS_MACOS: Final[bool] = platform.system() == "Darwin"
IS_LINUX: Final[bool] = platform.system() == "Linux"

# Environment-based constants with defaults
API_KEY: Final[str] = os.getenv("API_KEY", "default-key")
DEBUG: Final[bool] = os.getenv("DEBUG", "False").lower() == "true"

Constants in Configuration Classes

python
from typing import Final, Dict, Any

class AppConfig:
    """Application configuration constants."""
    
    # Server configuration
    HOST: Final[str] = "localhost"
    PORT: Final[int] = 8080
    DEBUG: Final[bool] = False
    
    # Database configuration
    DB_HOST: Final[str] = "localhost"
    DB_PORT: Final[int] = 5432
    DB_NAME: Final[str] = "myapp"
    
    # API configuration
    API_VERSION: Final[str] = "v1"
    API_RATE_LIMIT: Final[int] = 100
    
    @classmethod
    def get_database_url(cls) -> str:
        """Generate database URL from constants."""
        return f"postgresql://{cls.DB_HOST}:{cls.DB_PORT}/{cls.DB_NAME}"

Constants with Documentation

python
# Constants with comprehensive documentation
"""
Application Constants Module

This module contains all application-wide constants used throughout the system.
Constants are defined in uppercase to indicate they should not be modified.
"""

# API constants
API_BASE_URL: Final[str] = "https://api.example.com"
API_TIMEOUT: Final[int] = 30  # seconds
API_MAX_RETRIES: Final[int] = 3

# Business logic constants
MAX_ITEMS_PER_PAGE: Final[int] = 100
DEFAULT_ITEMS_PER_PAGE: Final[int] = 20
MIN_USERNAME_LENGTH: Final[int] = 3
MAX_USERNAME_LENGTH: Final[int] = 50

# System constants
LOG_LEVEL: Final[str] = "INFO"
LOG_FILE: Final[str] = "app.log"

Conclusion

Python doesn’t enforce constants at the language level like Java, but uses a convention-based approach that’s just as effective when followed properly. The key takeaways are:

  1. Use uppercase naming with underscores to indicate constants (e.g., MAX_CONNECTIONS)
  2. Follow PEP 8 guidelines for consistent code style across your project
  3. Consider typing.Final for better type checking and documentation (Python 3.8+)
  4. Group related constants in dedicated modules or classes for organization
  5. Document your constants to make their purpose and usage clear
  6. Respect the convention by not modifying declared constants, even though it’s technically possible

While Python gives you the flexibility to change “constants” at runtime, this flexibility comes with the responsibility to follow conventions that maintain code clarity and prevent bugs. The Python community’s emphasis on “consenting adults” means that developers are trusted to make good decisions about when and how to use constants.

For teams transitioning from Java to Python, the key mental shift is moving from language-enforced immutability to convention-based immutability. While the syntax is simpler in Python, the discipline required to maintain constants effectively is just as important.

Sources

  1. Python PEP 8 - Style Guide for Python Code
  2. Python typing.Final Documentation
  3. Python Enum Documentation
  4. Python Naming Conventions - Real Python
  5. Python Constants - GeeksforGeeks