Python – Naming Conventions
Python naming conventions come directly from PEP 8, the official style guide authored by Guido van Rossum. Following them ensures that Cygnus Dynamics Python code is immediately readable by any Python developer — internal or external — and works cleanly with tools like mypy, pylint, and IDEs.
Enforcement
These conventions are enforced automatically via pylint and flake8 in all Python projects. Your IDE (PyCharm or VS Code with Pylance) will highlight violations inline. Pre-commit hooks prevent non-conforming code from being committed.
Quick Reference
| Identifier | Convention | Example |
|---|---|---|
| Module / Package | snake_case (short, lowercase) |
order_service, user_utils |
| Class | PascalCase |
OrderService, PaymentProcessor |
| Exception | PascalCase + Error suffix |
OrderNotFoundError, ValidationError |
| Function | snake_case (verb) |
calculate_total(), fetch_user() |
| Method | snake_case (verb) |
get_order_by_id(), process_payment() |
| Variable | snake_case |
order_total, user_email_address |
| Constant | UPPER_SNAKE_CASE |
MAX_RETRY_COUNT, DEFAULT_TIMEOUT_SEC |
| Type variable | PascalCase short |
T, KT, AnyStr |
| Private (module) | _single_leading_underscore |
_internal_cache |
| Private (class) | __double_leading_underscore |
self.__order_id |
| Dunder / magic | __double_both_sides__ |
__init__, __str__ |
Packages and Modules
Package and module names must be short, all-lowercase. Use underscores to improve readability in module names — but avoid them in package (directory) names.
# ✅ Correct
import order_service
import user_utils
from cygnus.payments import gateway
# ❌ Incorrect
import OrderService # PascalCase not allowed
import order-service # hyphens not valid Python identifiers
import orderServiceUtils # camelCase not used in Python
Package structure at Cygnus Dynamics:
cygnus/
├── orders/ # package — no underscores
│ ├── __init__.py
│ ├── service.py # module — snake_case
│ ├── repository.py
│ └── models.py
├── payments/
│ ├── gateway.py
│ └── processor.py
└── common/
├── exceptions.py
└── validators.py
Classes
Class names use PascalCase and should be nouns that clearly describe what the class represents.
# ✅ PascalCase nouns
class OrderService:
pass
class PaymentProcessor:
pass
class UserProfileRepository:
pass
class HTTPRequestHandler: # Acronyms fully capitalised in PascalCase
pass
# ❌ Incorrect
class order_service: # snake_case — not allowed
class processOrders: # camelCase — not Python convention
class Mgr: # abbreviation — avoid
class DoTheThing: # verb phrase — use a noun
Common class naming patterns:
| Class Type | Naming Pattern | Example |
|---|---|---|
| Service | <Domain>Service |
OrderService |
| Repository | <Domain>Repository |
UserRepository |
| Model / Entity | <Domain> (plain noun) |
Order, User, Payment |
| Exception | <Description>Error |
OrderNotFoundError |
| Mixin | <Capability>Mixin |
TimestampMixin, AuditMixin |
| Abstract base | Base<Domain> or Abstract<Domain> |
BaseRepository |
| Data class | <Domain> or <Domain>Data |
OrderSummary, UserData |
| Configuration | <Domain>Config or <Domain>Settings |
DatabaseConfig |
| Enum | <Domain> (plain noun) |
OrderStatus, UserRole |
Exceptions
All custom exceptions must:
- Inherit from a built-in exception (
Exception,ValueError,RuntimeError, etc.) - End with the
Errorsuffix - Use
PascalCase
# ✅ Correct — descriptive name, Error suffix, appropriate base class
class OrderNotFoundError(Exception):
def __init__(self, order_id: int) -> None:
super().__init__(f"Order not found: order_id={order_id}")
self.order_id = order_id
class InsufficientStockError(ValueError):
def __init__(self, product_id: int, requested: int, available: int) -> None:
super().__init__(
f"Insufficient stock for product {product_id}: "
f"requested={requested}, available={available}"
)
self.product_id = product_id
class PaymentDeclinedError(RuntimeError):
pass
# ❌ Incorrect
class OrderNotFound(Exception): # missing Error suffix
pass
class order_not_found_error(Exception): # snake_case — wrong
pass
Functions and Methods
Function and method names must be snake_case and start with a verb that describes the action.
# ✅ Descriptive verb-first snake_case
def calculate_order_total(items: list) -> float:
pass
def fetch_user_by_email(email: str) -> "User":
pass
def validate_payment_details(card: dict) -> bool:
pass
def send_confirmation_email(user: "User", order: "Order") -> None:
pass
# ❌ Incorrect
def OrderTotal(items): # PascalCase is for classes
def getData(): # camelCase — not Python convention
def d(x): # single letter — meaningless
def order(id): # noun, not a verb
Boolean-returning Functions
Functions that return a boolean should be named as a clear yes/no question:
# ✅ Reads naturally in a conditional
def is_order_eligible_for_discount(order: "Order") -> bool:
pass
def has_active_subscription(user: "User") -> bool:
pass
def can_process_refund(order: "Order") -> bool:
pass
# Usage:
if is_order_eligible_for_discount(order):
apply_discount(order)
Method Argument Conventions
Always use self for instance methods and cls for class methods — no exceptions.
class OrderService:
def get_order(self, order_id: int) -> "Order": # instance method — self
pass
@classmethod
def from_config(cls, config: dict) -> "OrderService": # class method — cls
pass
@staticmethod
def validate_order_id(order_id: int) -> bool: # static — no self/cls
pass
If a parameter name clashes with a Python built-in or reserved keyword, append a single trailing underscore:
# ✅ Trailing underscore to avoid collision with 'type', 'id', 'class'
def create_event(type_: str, id_: int) -> None:
pass
# ❌ Corruption / abbreviation — worse than trailing underscore
def create_event(typ: str, eid: int) -> None:
pass
Variables
Variables use snake_case. Names must be meaningful — a reader should understand the value's purpose without reading the surrounding logic.
# ✅ Descriptive snake_case
order_total = calculate_total(cart_items)
active_user_count = len([u for u in users if u.is_active])
default_currency_code = "USD"
# ❌ Vague, wrong case, or abbreviated
t = calculate_total(cart) # single letter — meaningless
OrderTotal = calculate_total() # PascalCase is for classes
actUsrCnt = len(users) # camelCase abbreviation — not Pythonic
Names to Avoid Completely
Never use these as single-character variable names — in many fonts they are visually indistinguishable from digits:
| Name | Looks Like |
|---|---|
l (lowercase L) |
1 (the number one) |
O (uppercase O) |
0 (the number zero) |
I (uppercase I) |
1 (the number one) |
# ❌ Banned — visually ambiguous
l = 1
O = 0
I = 1
# ✅ Use descriptive names or safe alternatives
length = 1
count = 0
index = 1
Constants
Module-level constants are written in UPPER_SNAKE_CASE. They should be placed near the top of the module, after imports.
# ✅ Module-level constants — UPPER_SNAKE_CASE
MAX_RETRY_COUNT = 3
DEFAULT_PAGE_SIZE = 20
SESSION_TIMEOUT_SEC = 1800
API_BASE_URL = "https://api.cygnusdynamics.com/v2"
SUPPORTED_CURRENCIES = frozenset({"USD", "EUR", "GBP", "AED"})
# ✅ Use numeric underscores for readability
ONE_DAY_SECONDS = 86_400
MAX_UPLOAD_BYTES = 10_485_760 # 10 MB
# ❌ Wrong case — looks like a variable
max_retry_count = 3
MaxRetryCount = 3
Underscore Conventions
Python uses underscores to communicate visibility and intent. Understanding these is essential:
| Pattern | Meaning | Example |
|---|---|---|
_name |
Internal / non-public — convention only, still accessible | _cache, _helpers |
name_ |
Avoids clash with Python keyword or built-in | type_, id_, class_ |
__name |
Class-private — triggers name mangling | self.__order_id |
__name__ |
Dunder / magic — defined by Python protocol | __init__, __str__ |
class Order:
def __init__(self, order_id: int) -> None:
self.__order_id = order_id # __private — mangled to _Order__order_id
self._created_at = None # _internal — convention, not enforced
def __repr__(self) -> str: # __dunder__ — Python protocol
return f"Order(id={self.__order_id})"
def _build_summary(self) -> dict: # _internal method — not part of public API
pass
Never invent your own __dunder__ names
Dunder names (__like_this__) are reserved by the Python language. Only use them as documented by the data model. Creating your own __custom__ attributes risks clashing with future Python versions.
Type Variables
Type variables introduced with TypeVar use short PascalCase names. Add _co or _contra suffixes for covariant and contravariant type variables.