开发规范
本文档说明项目的代码规范、开发流程和最佳实践。
代码风格
Python 代码风格
遵循 PEP 8 代码风格指南,使用 Black 自动格式化。
主要规则:
- 使用 4 个空格缩进(不使用制表符)
- 行长度限制 88 字符(Black 默认)
- 使用有意义的变量名
- 添加类型注解
- 遵循 Google 风格的文档字符串
示例:
def calculate_total(items: list[dict[str, float]], tax_rate: float = 0.1) -> float:
"""
Calculate the total price including tax.
Args:
items: List of items with 'price' key
tax_rate: Tax rate as a decimal (default 0.1)
Returns:
Total price including tax
Raises:
ValueError: If any item has negative price
"""
if not items:
return 0.0
subtotal = sum(item['price'] for item in items)
if subtotal < 0:
raise ValueError("Total cannot be negative")
return subtotal * (1 + tax_rate)
命名规范
文件和目录:
- 使用小写字母
- 多个单词用下划线分隔:my_module.py
- 测试文件:test_module_name.py
变量和函数:
- 使用 snake_case:user_name, calculate_total()
类:
- 使用 PascalCase:UserProfile, DatabaseConnection
常量:
- 使用大写和下划线:MAX_RETRIES = 3
私有成员:
- 使用单下划线前缀:_internal_method()
- 使用双下划线前缀避免命名冲突:__class_attribute
文档字符串
Google 风格文档字符串
def process_data(input_data: dict[str, Any], validate: bool = True) -> dict[str, Any]:
"""
Process input data according to specified rules.
This function takes a dictionary of input data and processes it according
to the rules defined in the configuration.
Args:
input_data: Dictionary containing the input data to process.
Must contain 'id' and 'value' keys.
validate: Whether to validate the input data before processing.
Defaults to True.
Returns:
Dictionary containing the processed data with the following structure:
- 'id': Original ID
- 'processed_value': Processed value
- 'timestamp': Processing timestamp
Raises:
ValueError: If input_data is empty or missing required keys.
TypeError: If input_data is not a dictionary.
Example:
>>> data = {'id': '123', 'value': 'test'}
>>> result = process_data(data)
>>> print(result['id'])
'123'
Note:
This function modifies the input dictionary in place if validate=False.
"""
if not isinstance(input_data, dict):
raise TypeError("input_data must be a dictionary")
if not input_data:
raise ValueError("input_data cannot be empty")
# Implementation here
return processed_result
类文档字符串
class UserManager:
"""
Manages user accounts and authentication.
This class provides methods for creating, updating, and deleting user
accounts, as well as handling authentication and authorization.
Attributes:
db_connection: Database connection instance
cache: Cache instance for storing user sessions
Example:
>>> manager = UserManager(db_connection)
>>> user = manager.create_user('john@example.com', 'password123')
>>> print(user.id)
'user_123'
"""
def __init__(self, db_connection: Database, cache: Cache) -> None:
"""
Initialize UserManager.
Args:
db_connection: Database connection instance
cache: Cache instance for storing user sessions
"""
self.db_connection = db_connection
self.cache = cache
类型注解
基本类型
# 基本类型
name: str = "John"
age: int = 30
price: float = 19.99
is_active: bool = True
# 容器类型(Python 3.9+)
items: list[str] = ["apple", "banana"]
counts: dict[str, int] = {"apple": 5, "banana": 3}
# 容器类型(Python 3.8 及更早)
from typing import List, Dict
items: List[str] = ["apple", "banana"]
counts: Dict[str, int] = {"apple": 5}
可选类型
from typing import Optional
def find_user(user_id: str) -> Optional[dict]:
"""
Find user by ID.
Args:
user_id: User identifier
Returns:
User dictionary if found, None otherwise
"""
# Implementation
pass
多种类型
from typing import Union
def process_value(value: Union[int, str]) -> str:
"""Process integer or string value."""
return str(value)
泛型函数
from typing import TypeVar, List
T = TypeVar('T')
def reverse(items: List[T]) -> List[T]:
"""Reverse a list of items."""
return items[::-1]
Git 工作流
分支命名规范
feature/add-user-authentication
bugfix/fix-login-error
hotfix/critical-security-patch
refactor/improve-api-structure
docs/update-readme
test/add-integration-tests
提交消息规范
格式:
类型:
feat: 新功能fix: Bug 修复docs: 文档更改style: 代码格式(不影响代码运行)refactor: 重构test: 添加或修改测试chore: 构建过程或辅助工具的变动
示例:
# 简单提交
git commit -m "feat: add user authentication"
# 详细提交
git commit -m "feat(auth): add OAuth2 login support
- Implement OAuth2 authentication flow
- Add token refresh mechanism
- Update user session management
Closes #123"
Pre-commit 检查
提交前会自动运行以下检查:
- black - 代码格式化
- ruff - 代码检查
- flake8 - 代码风格
- mypy - 类型检查
- interrogate - 文档覆盖率
- isort - Import 排序
跳过检查(不推荐):
测试规范
测试文件组织
tests/
├── __init__.py
├── conftest.py # Pytest 配置
├── test_core.py # 核心功能测试
├── test_api.py # API 测试
└── integration/ # 集成测试
└── test_integration.py
编写测试
import pytest
from src.core.example import Example
class TestExample:
"""Test suite for Example class."""
def test_init(self):
"""Test Example initialization."""
example = Example(42)
assert example.value == 42
def test_method_with_valid_input(self):
"""Test method with valid input."""
example = Example(10)
result = example.calculate(2)
assert result == 20
def test_method_with_invalid_input(self):
"""Test method with invalid input raises error."""
example = Example(10)
with pytest.raises(ValueError):
example.calculate(-1)
@pytest.mark.parametrize("input_value,expected", [
(0, 0),
(5, 25),
(10, 100),
])
def test_calculate_multiple(self, input_value, expected):
"""Test calculate with multiple inputs."""
example = Example(input_value)
assert example.calculate(input_value) == expected
测试覆盖率要求
- 最低覆盖率:80%
- 核心功能:100%
- 使用
interrogate检查文档覆盖率
代码审查
Pull Request 检查清单
功能: - [ ] 代码实现符合需求 - [ ] 没有破坏现有功能 - [ ] 添加了适当的错误处理
代码质量: - [ ] 代码符合风格指南 - [ ] 通过了所有 lint 检查 - [ ] 代码已格式化 - [ ] 没有类型错误
测试: - [ ] 添加了单元测试 - [ ] 测试通过 - [ ] 覆盖率满足要求 - [ ] 集成测试通过(如适用)
文档: - [ ] 添加了文档字符串 - [ ] 更新了 README(如需要) - [ ] 更新了 CHANGELOG(如需要)
其他: - [ ] 没有提交敏感信息 - [ ] 没有提交临时文件 - [ ] CI 检查通过
性能优化
代码性能
- 使用生成器 节省内存
- 缓存计算结果 避免重复计算
- 使用适当的数据结构 提高查找速度
- 异步 I/O 提高并发性能
# 好的实践
def process_large_dataset(data):
"""Process large dataset using generator."""
for item in data:
yield process_item(item)
# 避免
def process_large_dataset(data):
"""Process large dataset."""
results = []
for item in data:
results.append(process_item(item))
return results
性能分析
使用 snakeviz 进行性能分析:
import cProfile
import pstats
def profile_code(func):
"""Profile a function."""
profiler = cProfile.Profile()
profiler.enable()
result = func()
profiler.disable()
profiler.dump_stats('profile.stats')
return result
最佳实践
错误处理
import logging
logger = logging.getLogger(__name__)
def process_user_data(user_id: str) -> dict:
"""Process user data with proper error handling."""
try:
user = get_user(user_id)
return process(user)
except UserNotFoundError:
logger.error(f"User {user_id} not found")
raise
except Exception as e:
logger.error(f"Unexpected error processing user {user_id}: {e}")
raise ProcessingError(f"Failed to process user: {e}") from e
日志记录
import logging
logger = logging.getLogger(__name__)
def important_operation(value: str) -> None:
"""Perform important operation with logging."""
logger.info(f"Starting important operation with value: {value}")
try:
result = do_something(value)
logger.info(f"Operation completed successfully")
except Exception as e:
logger.error(f"Operation failed: {e}", exc_info=True)
raise
资源管理
from contextlib import contextmanager
@contextmanager
def managed_resource(resource):
"""Context manager for resource management."""
try:
resource.open()
yield resource
finally:
resource.close()
# 使用
with managed_resource(resource):
# 使用资源
pass