Python Coding Standards
This page was generated by AI and has been manually reviewed and edited.
This document standardizes code style, toolchain, and commit workflows for Python projects, reducing collaboration overhead and improving maintainability.
1. Code Organization
1.1 Module Design
- Each module has a single responsibility; avoid cramming too many features into one file
- Split packages by functionality; avoid catch-all packages like util/common/misc
- Types and functions with similar functionality go in the same module
- Avoid circular imports; mark internal implementations as private with a
_prefix
1.2 File Organization
- Order within a file: shebang → encoding declaration → module docstring → import → constants → variables → functions → classes
- One file focuses on one core class or feature group
python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""User service module, providing user-related business logic and data access."""
from __future__ import annotations
import logging
from datetime import datetime
from pydantic import BaseModel
from .repository import UserRepository
MAX_RETRY_COUNT = 3
logger = logging.getLogger(__name__)
class User(BaseModel):
id: int
name: str
def get_user(user_id: int) -> User | None:
...2. Naming Conventions
2.1 Basic Rules
- Modules/packages: short lowercase names, may use underscores as separators (
user_service.py) - Classes: PascalCase (
UserService,OrderProcessor) - Functions/methods/variables: snake_case (
get_user,item_count) - Constants: UPPER_SNAKE_CASE (
MAX_RETRY_COUNT,API_BASE_URL)
2.2 Private Members
- Non-public attributes use a single underscore prefix (
_internal_method,_cache) - Avoid double underscore prefixes unless name mangling is genuinely needed to prevent subclass conflicts
2.3 Boolean Variables
- Use
is_/has_/can_prefixes to indicate boolean meaning
python
is_active: bool
has_permission: bool
can_edit: bool3. Type Annotations
3.1 Basic Requirements
- All new code must include type annotations
- Public interfaces must provide complete function signature annotations
- Python 3.10+ use
X | Noneinstead ofOptional[X] - Python 3.9+ use built-in generics
list[X],dict[K, V]; deprecatetyping.List,typing.Dict
3.2 Complex Types
- Prefer
TypedDict,Protocol,dataclassfor complex structures - Avoid excessive
dict[str, Any]; use structured types instead
python
from dataclasses import dataclass
from typing import TypedDict
@dataclass(slots=True)
class User:
id: int
name: str
class UserDict(TypedDict):
id: int
name: str
def find_user_name(users: list[User], user_id: int) -> str | None:
for user in users:
if user.id == user_id:
return user.name
return None3.3 Type Checking
- Recommend using mypy
--strictmode
bash
mypy --strict src/4. Import Conventions
4.1 Import Order
- Standard library → third-party libraries → local modules, separated by blank lines
- Each import on its own line
python
import os
import sys
from pathlib import Path
from pydantic import BaseModel
from sqlalchemy import select
from .models import User
from .service import UserService4.2 Prohibited Actions
- No
from module import *(except for modules with explicitly declared__all__) - No circular imports
- No relative imports using
..that cross package boundaries
5. Strings and Formatting
5.1 String Usage
- Prefer f-strings (Python 3.6+)
- Triple-quoted strings for multi-line strings
- Avoid string concatenation in loops; use
"".join()orStringIO
python
name = "Alice"
msg = f"Hello, {name}!"
query = """
SELECT id, name
FROM users
WHERE active = true
"""5.2 Docstring
- Modules, classes, and public functions must have docstrings
- Use triple quotes; first line is a brief description, followed by a blank line and detailed explanation
- Recommend Google or NumPy style
python
def get_user(user_id: int) -> User | None:
"""Get a user by ID.
Args:
user_id: The unique identifier of the user.
Returns:
The matching User object, or None if not found.
Raises:
ValueError: Raised when user_id is not a positive integer.
"""
if user_id <= 0:
raise ValueError("user_id must be positive")
...6. Error Handling
6.1 Basic Principles
- Catch specific exceptions; no bare
except:orexcept Exception: - Custom exceptions inherit from
Exceptionand provide meaningful error messages - Never silently swallow exceptions in except blocks
6.2 Exception Design
python
class UserNotFoundError(Exception):
"""Exception raised when a user is not found."""
def __init__(self, user_id: int):
self.user_id = user_id
super().__init__(f"User {user_id} not found")
class ValidationError(Exception):
"""Exception raised for data validation errors."""
...6.3 Resource Management
- Use context managers (
with) for managing file, connection, and other resources - Custom resources implement
__enter__and__exit__
python
with open("data.txt") as f:
content = f.read()7. Functions and Methods
7.1 Function Design
- Keep functions as small as possible; recommend no more than 50 lines
- One function does one thing
- When there are too many parameters, consider encapsulating them with dataclass or TypedDict
7.2 Default Parameters
- Default parameter values must be immutable; never
def f(x=[])ordef f(x={})
python
# Correct
def append_item(item: str, items: list[str] | None = None) -> list[str]:
if items is None:
items = []
items.append(item)
return items
# Incorrect
def append_item(item: str, items: list[str] = []) -> list[str]:
items.append(item)
return items7.3 Function Style
- Use keyword arguments to improve readability
- Prefer
deffor defining functions; never assign a lambda to a variable
python
# Correct
def is_active(user: User) -> bool:
return user.status == "active"
# Incorrect
is_active = lambda user: user.status == "active"8. Class Design
8.1 Design Principles
- Use
@propertyinstead of Java-style getters/setters - Use
@dataclassto simplify data classes - Prefer composition over inheritance
- Ensure MRO is clear and understandable when using multiple inheritance
8.2 Special Methods
__str__is user-facing, returning a highly readable description__repr__is developer-facing, ideally returning an expression that can reconstruct the object
python
from dataclasses import dataclass
@dataclass(slots=True)
class User:
id: int
name: str
email: str
@property
def display_name(self) -> str:
return self.name or self.email
def __str__(self) -> str:
return f"User({self.display_name})"
def __repr__(self) -> str:
return f"User(id={self.id!r}, name={self.name!r}, email={self.email!r})"9. Concurrency and Async
9.1 Async Programming
- IO-intensive tasks should prefer
async/await - Use
asyncio.gatherfor concurrent execution of multiple coroutines - Use
asyncio.to_threadto wrap synchronous blocking calls
python
import asyncio
async def fetch_all(urls: list[str]) -> list[str]:
async def fetch(url: str) -> str:
...
return ""
return await asyncio.gather(*(fetch(url) for url in urls))9.2 Thread Safety
- Use
threading.Lockto protect shared state in multi-threaded scenarios - Use
queue.Queuefor inter-thread communication - Never use
time.sleepin async code; useawait asyncio.sleep
10. Testing Conventions
10.1 Framework and Organization
- Use
pytestas the testing framework - Test file naming:
test_*.py; test functions prefixed withtest_ - One test verifies one primary behavior
- Bug fixes must include test cases
10.2 Testing Practices
- Use fixtures instead of setUp/tearDown
- Use
pytest.mark.parametrizefor parameterized tests - Mock external dependencies using
unittest.mockorpytest-mock
python
import pytest
from unittest.mock import Mock
from .service import UserService
@pytest.fixture
def user_service() -> UserService:
repo = Mock()
return UserService(repo)
def test_get_user_returns_none_when_not_found(user_service: UserService) -> None:
user_service.repo.find_by_id.return_value = None
assert user_service.get_user(1) is None
@pytest.mark.parametrize(
("user_id", "expected"),
[(0, False), (-1, False), (1, True)],
)
def test_valid_user_id(user_id: int, expected: bool) -> None:
assert is_valid_user_id(user_id) == expected11. Toolchain and Formatting
11.1 Recommended Toolchain
- Python:
3.13+ - Package management:
uv - Formatting and linting:
ruff - Type checking:
tyormypy - Testing:
pytest - Pre-commit hooks:
pre-commit
11.2 Common Commands
bash
ruff format
ruff check
ruff check --fix
ty check
pytestruff formathandles code formattingruff checkhandles style and common issue checks--fixcan auto-fix some issues- All checks must pass in CI
12. Prohibited Actions
- No bare
except:orexcept Exception: - No mutable default parameters (
def f(x=[])) - No modifying global variables inside functions
- No hardcoding sensitive information such as keys or credentials
- No using
assertfor business logic validation - No
from module import * - No circular imports
- No assigning a lambda to a variable
- No silently swallowing exceptions
- No using
time.sleepin async code