Python Identifiers and Naming Conventions — PEP 8 Guide
Advertisement
Introduction
Why This Matters
In Python, every name you give to a variable, function, class, or module is an identifier. Python's rules for what constitutes a valid identifier are strict — break them and your code won't run. But beyond correctness, how you name things is equally important for readability and professionalism.
The Python community follows PEP 8, the official style guide, which establishes naming conventions that are used across the entire ecosystem. When you contribute to open-source projects, pass code review, or work in a team, following PEP 8 naming is not optional — it is expected. Poor naming is one of the most common code review comments and one of the easiest things to fix.
Understanding naming also unlocks Python's special underscore conventions — _private, __dunder__, __mangled — which carry semantic meaning in classes and modules.
Rules for Valid Identifiers
An identifier must:
- Start with a letter (
a-z,A-Z) or underscore_ - Contain only letters, digits (
0-9), or underscores - Not be a Python keyword
# Valid identifiers
user_name = "Alice"
_count = 0
MAX_SIZE = 100
UserProfile = object()
x1 = 10
# Invalid identifiers
# 1user = "Alice" — starts with digit
# user-name = "Bob" — hyphen not allowed
# class = "A" — reserved keywordPython Keywords
These are reserved and cannot be used as identifiers:
False None True and as assert async await
break class continue def del elif else except
finally for from global if import in is
lambda nonlocal not or pass raise return try
while with yieldimport keyword
print(keyword.kwlist) # full list of reserved keywordsPEP 8 Naming Conventions
| Entity | Convention | Example |
|---|---|---|
| Variable | snake_case | user_name, total_count |
| Function | snake_case | calculate_tax(), get_user() |
| Class | PascalCase | UserProfile, HttpClient |
| Constant | UPPER_SNAKE_CASE | MAX_SIZE, API_URL |
| Module | snake_case | user_utils.py |
| Package | lowercase | mypackage/ |
# Variables and functions: snake_case
user_count = 42
first_name = "Alice"
def calculate_area(radius: float) -> float:
return 3.14159 * radius ** 2
# Classes: PascalCase
class DatabaseConnection:
pass
class HttpRequestHandler:
pass
# Constants: UPPER_SNAKE_CASE (convention only — not enforced)
MAX_RETRIES = 3
DEFAULT_TIMEOUT = 30
API_BASE_URL = "https://api.example.com"Underscore Conventions
Python uses underscores to convey special meaning:
# Single leading underscore: internal/private by convention
class MyClass:
def __init__(self):
self._internal_state = "private" # hint: don't access externally
def _helper(self):
pass # internal method
# Double leading underscore: name mangling (harder to access from subclasses)
class Base:
def __init__(self):
self.__secret = "mangled" # stored as _Base__secret
# Double leading and trailing: dunder (magic) methods
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Vector({self.x}, {self.y})"
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
# Single trailing underscore: avoid conflict with keyword
class_ = "Python101" # avoid shadowing 'class' keyword
type_ = "integer"Module-Level Identifiers
# __all__ controls what's exported with 'from module import *'
__all__ = ["PublicClass", "public_function"]
# Module metadata
__version__ = "1.2.3"
__author__ = "Alice"
def public_function():
pass
def _private_function():
passType Alias Naming (Python 3.12+)
from typing import TypeAlias
UserId: TypeAlias = int
UserMap: TypeAlias = dict[int, str]
# Python 3.12+ type statement
type Matrix = list[list[float]]Common Mistakes
- Using
camelCasefor functions/variables instead ofsnake_case - Naming a variable
list,dict,type, orid— shadows built-ins - Using single-letter names (except
i,j,kin loops orx,yfor coordinates) - Inconsistent naming style across a module
- Using double underscores (
__name) without understanding name mangling
Best Practices
- Follow PEP 8 naming conventions consistently throughout your project
- Use descriptive names —
user_countis better thanucorn - Avoid abbreviations unless they are universally understood (
url,db,api) - Run
pycodestyleorruffto enforce naming conventions automatically - Use
__all__in modules to explicitly declare the public API
Key Takeaways
- Identifiers must start with a letter or underscore, contain only letters/digits/underscores
- Python has 35 reserved keywords that cannot be used as identifiers
- Variables and functions use
snake_case; classes usePascalCase; constants useUPPER_SNAKE_CASE - A single leading underscore
_namesignals private by convention; Python does not enforce it - Double leading underscores
__nametrigger name mangling for subclass protection - Dunder methods (
__init__,__repr__) are Python magic methods — never create your own - Using built-in names as identifiers (
list = []) is valid but hides the built-in type
Advertisement