name: python-anti-patterns description: 避免的常见Python反模式。在审查代码、最终确定实现或调试可能源于已知不良实践的问题时用作检查清单。
Python反模式检查清单
一个Python代码中常见错误和反模式的参考检查清单。在最终确定实现前审查此清单,以早期发现问题。
何时使用此技能
- 合并前审查代码
- 调试神秘问题
- 教学或学习Python最佳实践
- 建立团队编码标准
- 重构遗留代码
注意: 此技能侧重于避免什么。关于积极模式和架构的指导,请参见python-design-patterns技能。
基础设施反模式
分散的超时/重试逻辑
# 坏:超时逻辑到处重复
def fetch_user(user_id):
try:
return requests.get(url, timeout=30)
except Timeout:
logger.warning("获取用户超时")
return None
def fetch_orders(user_id):
try:
return requests.get(url, timeout=30)
except Timeout:
logger.warning("获取订单超时")
return None
修复: 集中在装饰器或客户端包装器中。
# 好:集中重试逻辑
@retry(stop=stop_after_attempt(3), wait=wait_exponential())
def http_get(url: str) -> Response:
return requests.get(url, timeout=30)
双重重试
# 坏:在多个层重试
@retry(max_attempts=3) # 应用重试
def call_service():
return client.request() # 客户端也有重试配置!
修复: 只在一个层重试。了解基础设施的重试行为。
硬编码配置
# 坏:代码中的秘密和配置
DB_HOST = "prod-db.example.com"
API_KEY = "sk-12345"
def connect():
return psycopg.connect(f"host={DB_HOST}...")
修复: 使用环境变量和类型化设置。
# 好
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
db_host: str = Field(alias="DB_HOST")
api_key: str = Field(alias="API_KEY")
settings = Settings()
架构反模式
暴露内部类型
# 坏:将ORM模型泄露给API
@app.get("/users/{id}")
def get_user(id: str) -> UserModel: # SQLAlchemy模型
return db.query(UserModel).get(id)
修复: 使用DTO/响应模型。
# 好
@app.get("/users/{id}")
def get_user(id: str) -> UserResponse:
user = db.query(UserModel).get(id)
return UserResponse.from_orm(user)
混合I/O和业务逻辑
# 坏:SQL嵌入在业务逻辑中
def calculate_discount(user_id: str) -> float:
user = db.query("SELECT * FROM users WHERE id = ?", user_id)
orders = db.query("SELECT * FROM orders WHERE user_id = ?", user_id)
# 业务逻辑与数据访问混合
if len(orders) > 10:
return 0.15
return 0.0
修复: 仓储模式。保持业务逻辑纯净。
# 好
def calculate_discount(user: User, orders: list[Order]) -> float:
# 纯净业务逻辑,易于测试
if len(orders) > 10:
return 0.15
return 0.0
错误处理反模式
裸异常处理
# 坏:吞掉所有异常
try:
process()
except Exception:
pass # 静默失败 - 错误永远隐藏
修复: 捕获特定异常。适当记录或处理。
# 好
try:
process()
except ConnectionError as e:
logger.warning("连接失败,将重试", error=str(e))
raise
except ValueError as e:
logger.error("无效输入", error=str(e))
raise BadRequestError(str(e))
忽略部分失败
# 坏:在第一个错误处停止
def process_batch(items):
results = []
for item in items:
result = process(item) # 错误时抛出 - 批次中止
results.append(result)
return results
修复: 捕获成功和失败。
# 好
def process_batch(items) -> BatchResult:
succeeded = {}
failed = {}
for idx, item in enumerate(items):
try:
succeeded[idx] = process(item)
except Exception as e:
failed[idx] = e
return BatchResult(succeeded, failed)
缺少输入验证
# 坏:无验证
def create_user(data: dict):
return User(**data) # 在坏输入时深入代码崩溃
修复: 在API边界早期验证。
# 好
def create_user(data: dict) -> User:
validated = CreateUserInput.model_validate(data)
return User.from_input(validated)
资源反模式
未关闭资源
# 坏:文件从未关闭
def read_file(path):
f = open(path)
return f.read() # 如果这里抛出呢?
修复: 使用上下文管理器。
# 好
def read_file(path):
with open(path) as f:
return f.read()
异步中的阻塞
# 坏:阻塞整个事件循环
async def fetch_data():
time.sleep(1) # 阻塞一切!
response = requests.get(url) # 也阻塞!
修复: 使用异步原生库。
# 好
async def fetch_data():
await asyncio.sleep(1)
async with httpx.AsyncClient() as client:
response = await client.get(url)
类型安全反模式
缺少类型提示
# 坏:无类型
def process(data):
return data["value"] * 2
修复: 注释所有公共函数。
# 好
def process(data: dict[str, int]) -> int:
return data["value"] * 2
无类型集合
# 坏:没有类型参数的泛型列表
def get_users() -> list:
...
修复: 使用类型参数。
# 好
def get_users() -> list[User]:
...
测试反模式
只测试快乐路径
# 坏:只测试成功案例
def test_create_user():
user = service.create_user(valid_data)
assert user.id is not None
修复: 测试错误条件和边缘情况。
# 好
def test_create_user_success():
user = service.create_user(valid_data)
assert user.id is not None
def test_create_user_invalid_email():
with pytest.raises(ValueError, match="Invalid email"):
service.create_user(invalid_email_data)
def test_create_user_duplicate_email():
service.create_user(valid_data)
with pytest.raises(ConflictError):
service.create_user(valid_data)
过度模拟
# 坏:模拟一切
def test_user_service():
mock_repo = Mock()
mock_cache = Mock()
mock_logger = Mock()
mock_metrics = Mock()
# 测试不验证真实行为
修复: 对关键路径使用集成测试。只模拟外部服务。
快速审查检查清单
在最终确定代码前,验证:
- [ ] 无分散的超时/重试逻辑(集中化)
- [ ] 无双重重试(应用+基础设施)
- [ ] 无硬编码配置或秘密
- [ ] 无暴露内部类型(ORM模型、protobufs)
- [ ] 无混合I/O和业务逻辑
- [ ] 无裸
except Exception: pass - [ ] 无忽略批次中的部分失败
- [ ] 无缺少输入验证
- [ ] 无未关闭资源(使用上下文管理器)
- [ ] 无异步中的阻塞调用
- [ ] 所有公共函数都有类型提示
- [ ] 集合有类型参数
- [ ] 错误路径经过测试
- [ ] 边缘情况覆盖
常见修复摘要
| 反模式 | 修复 |
|---|---|
| 分散重试逻辑 | 集中装饰器 |
| 硬编码配置 | 环境变量 + pydantic-settings |
| 暴露ORM模型 | DTO/响应模式 |
| 混合I/O + 逻辑 | 仓储模式 |
| 裸异常 | 捕获特定异常 |
| 批次因错误停止 | 返回BatchResult包含成功/失败 |
| 无验证 | 使用Pydantic在边界验证 |
| 未关闭资源 | 上下文管理器 |
| 异步中阻塞 | 异步原生库 |
| 缺少类型 | 所有公共API的类型注释 |
| 只快乐路径测试 | 测试错误和边缘情况 |