Naming Conventions, Constants, and Unpacking

Updated

September 7, 2026

Naming Conventions, Constants, and Unpacking

After reading this chapter, you will write clean, idiomatic PEP 8 Python code, leverage sequence unpacking, and understand how constants are declared and enforced.

Mental model

Python does not have a language-level const keyword. Instead, constant conventions are enforced through naming standards and static analysis tools like ruff and mypy.

Naming Categories:
┌──────────────────────────┬────────────────────────────┬────────────────────────┐
│ Pattern                  │ Convention                 │ Example                │
├──────────────────────────┼────────────────────────────┼────────────────────────┤
│ snake_case               │ Functions, variables, files│ db_connection_timeout  │
│ UPPER_SNAKE_CASE         │ Constants, globals         │ MAX_RETRIES, HTTP_OK   │
│ PascalCase (CapWords)    │ Classes, Exception types   │ DatabasePool, APIError │
│ _leading_underscore      │ Internal / Private marker  │ _internal_buffer       │
│ __dunder__               │ Special Python protocols   │ __init__, __repr__     │
└──────────────────────────┴────────────────────────────┴────────────────────────┘

Unpacking allows you to extract elements from an iterable directly into named variables in a single atomic statement without manual index indexing.


Minimal example

Save as unpacking_demo.py:

# unpacking_demo.py
def main() -> None:
    # Atomic variable swap without temporary variable
    left = "left_hand"
    right = "right_hand"
    left, right = right, left
    print(f"Swapped: left={left}, right={right}")

    # Extended star unpacking
    endpoint_data = ("GET", "/api/v1/users", 200, "0.12ms", "192.168.1.10")
    method, path, status, *telemetry = endpoint_data

    print(f"Method    : {method}")
    print(f"Path      : {path}")
    print(f"Status    : {status}")
    print(f"Telemetry : {telemetry} (packed into a list)")

if __name__ == "__main__":
    main()

Run via uv run python unpacking_demo.py:

Swapped: left=right_hand, right=left_hand
Method    : GET
Path      : /api/v1/users
Status    : 200
Telemetry : ['0.12ms', '192.168.1.10'] (packed into a list)

Worked examples

Case 1: Star unpacking in loops and data processing

You can cleanly separate headers, body items, and footers from structured data stream records:

# process_records.py
def process_stream() -> None:
    records = [
        ("auth", "admin", "success", "10.0.0.1"),
        ("query", "select * from users", "success", "10.0.0.2"),
        ("error", "timeout on connection", "failed", "10.0.0.3"),
    ]

    for event_type, *details, ip in records:
        print(f"Event: {event_type:8s} | IP: {ip:12s} | Details: {' '.join(details)}")

if __name__ == "__main__":
    process_stream()

Run:

uv run python process_records.py

Case 2: Marking constant intent with typing.Final

While CPython allows rebinding uppercase variables, modern type checkers will flag modifications to variables marked with Final:

# constants_guard.py
from typing import Final

DATABASE_TIMEOUT_SECONDS: Final[int] = 30
MAX_CONNECTIONS: Final[int] = 100

def check_limits(requested: int) -> bool:
    return requested <= MAX_CONNECTIONS

if __name__ == "__main__":
    print(f"Timeout: {DATABASE_TIMEOUT_SECONDS}s")
    print(f"Acceptable: {check_limits(50)}")

Run:

uv run python constants_guard.py

Pitfalls

Pitfall 1: Unbalanced unpacking assignments

coords = (10, 20, 30)
x, y = coords  # ValueError: too many values to unpack (expected 2, got 3)

# Fix: Match length or use star expression:
x, y, _ = coords  # Ignore the 3rd value using dummy variable "_"
x, *rest = coords # Capture remaining values

Pitfall 2: Overriding built-in functions with variable names

Never name variables after built-in functions like id, type, list, str, dict, or min:

# Dangerous:
list = [1, 2, 3]  # Now the built-in "list()" constructor is masked in this scope!

Exercises

  1. Write a script that unpacks a 5-element tuple into first, middle (a list of 3 elements using *), and last.
  2. Write a script that unpacks nested coordinates: entry = ("datacenter-east", (40.7128, -74.0060)). Extract the name and latitude/longitude in a single unpacking line.
  3. Use ruff check on a file containing non-PEP8 variable names (like camelCaseVariable = 10) to see how modern linters catch naming deviations.

Further reading

  • PEP 8: Style Guide for Python Code (Naming Conventions section).
  • PEP 3132: Extended Iterable Unpacking.
  • Python Standard Library: typing.Final.