关于 python-anti-patterns
这是面向 Python 开发者的代码审查参考技能,可在合并前、调试时或重构期间查找常见实现问题。它按领域整理反模式,配有改进建议和快速审查清单。
主要功能
- 01按代码领域分类的检查清单
- 02问题模式与改进示例
- 03错误处理和资源管理建议
- 04异步代码与类型安全提示
- 05Python 特有陷阱说明
适用场景
- 01合并前审查 Python 代码
- 02调查反复出现的运行问题
- 03教授 Python 实践或制定团队规范
- 04重构时评估遗留代码
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ julianobarbosa/ claude-code-skills --skill python-anti-patterns安装后在 Claude Code 中输入/python-anti-patterns确认已加载。
详细介绍
Python 反模式检查清单
常见 Python 代码错误与反模式的参考检查清单。完成实现前先检查,以便及早发现问题。
何时使用此技能
- 合并前审查代码
- 调试难以解释的问题
- 教授或学习 Python 最佳实践
- 制定团队编码规范
- 重构遗留代码
注意: 此技能侧重于应避免的做法。有关正向模式和架构的指导,请参阅 python-design-patterns 技能。
基础设施反模式
分散的超时/重试逻辑
# BAD: Timeout logic duplicated everywhere
def fetch_user(user_id):
try:
return requests.get(url, timeout=30)
except Timeout:
logger.warning("Timeout fetching user")
return None
def fetch_orders(user_id):
try:
return requests.get(url, timeout=30)
except Timeout:
logger.warning("Timeout fetching orders")
return None
修复: 通过装饰器或客户端包装器集中处理。
# GOOD: Centralized retry logic
@retry(stop=stop_after_attempt(3), wait=wait_exponential())
def http_get(url: str) -> Response:
return requests.get(url, timeout=30)
双重重试
# BAD: Retrying at multiple layers
@retry(max_attempts=3) # Application retry
def call_service():
return client.request() # Client also has retry configured!
修复: 只在一个层级重试。了解基础设施的重试行为。
硬编码配置
# BAD: Secrets and config in code
DB_HOST = "prod-db.example.com"
API_KEY = "sk-12345"
def connect():
return psycopg.connect(f"host={DB_HOST}...")
修复: 使用带类型的设置和环境变量。
# GOOD
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
db_host: str = Field(alias="DB_HOST")
api_key: str = Field(alias="API_KEY")
settings = Settings()
架构反模式
暴露内部类型
# BAD: Leaking ORM model to API
@app.get("/users/{id}")
def get_user(id: str) -> UserModel: # SQLAlchemy model
return db.query(UserModel).get(id)
修复: 使用 DTO/响应模型。
# GOOD
@app.get("/users/{id}")
def get_user(id: str) -> UserResponse:
user = db.query(UserModel).get(id)
return UserResponse.from_orm(user)
I/O 与业务逻辑混杂
# BAD: SQL embedded in business logic
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)
# Business logic mixed with data access
if len(orders) > 10:
return 0.15
return 0.0
修复: 使用仓储模式。保持业务逻辑纯粹。
# GOOD
def calculate_discount(user: User, orders: list[Order]) -> float:
# Pure business logic, easily testable
if len(orders) > 10:
return 0.15
return 0.0
错误处理反模式
裸异常处理
# BAD: Swallowing all exceptions
try:
process()
except Exception:
pass # Silent failure - bugs hidden forever
修复: 捕获特定异常,并妥善记录或处理。
# GOOD
try:
process()
except ConnectionError as e:
logger.warning("Connection failed, will retry", error=str(e))
raise
except ValueError as e:
logger.error("Invalid input", error=str(e))
raise BadRequestError(str(e))
忽略部分失败
# BAD: Stops on first error
def process_batch(items):
results = []
for item in items:
result = process(item) # Raises on error - batch aborted
results.append(result)
return results
修复: 同时记录成功和失败。
# GOOD
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)
缺少输入验证
# BAD: No validation
def create_user(data: dict):
return User(**data) # Crashes deep in code on bad input
修复: 在 API 边界尽早进行验证。
# GOOD
def create_user(data: dict) -> User:
validated = CreateUserInput.model_validate(data)
return User.from_input(validated)
资源反模式
未关闭资源
# BAD: File never closed
def read_file(path):
f = open(path)
return f.read() # What if this raises?
修复: 使用上下文管理器。
# GOOD
def read_file(path):
with open(path) as f:
return f.read()
异步代码中执行阻塞操作
# BAD: Blocks the entire event loop
async def fetch_data():
time.sleep(1) # Blocks everything!
response = requests.get(url) # Also blocks!
修复: 使用原生支持异步的库。
# GOOD
async def fetch_data():
await asyncio.sleep(1)
async with httpx.AsyncClient() as client:
response = await client.get(url)
类型安全反模式
缺少类型提示
# BAD: No types
def process(data):
return data["value"] * 2
修复: 为所有公共函数添加注解。
# GOOD
def process(data: dict[str, int]) -> int:
return data["value"] * 2
集合未标注类型
# BAD: Generic list without type parameter
def get_users() -> list:
...
修复: 使用类型参数。
# GOOD
def get_users() -> list[User]:
...
测试反模式
只测试正常路径
# BAD: Only tests success case
def test_create_user():
user = service.create_user(valid_data)
assert user.id is not None
修复: 测试错误条件和边界情况。
# GOOD
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)
过度模拟
# BAD: Mocking everything
def test_user_service():
mock_repo = Mock()
mock_cache = Mock()
mock_logger = Mock()
mock_metrics = Mock()
# Test doesn't verify real behavior
修复: 为关键路径编写集成测试。只模拟外部服务。
快速审查清单
完成代码前,请确认:
- 没有分散的超时/重试逻辑(已集中处理)
- 没有双重重试(应用 + 基础设施)
- 没有硬编码配置或机密信息
- 没有暴露内部类型(ORM 模型、protobuf)
- I/O 与业务逻辑没有混杂
- 没有裸
except Exception: pass - 批处理中没有忽略部分失败
- 输入验证没有缺失
- 所有资源都已关闭(使用上下文管理器)
- 异步代码中没有阻塞调用
- 所有公共函数都有类型提示
- 集合都有类型参数
- 已测试错误处理路径
- 已覆盖边界情况
常见修复汇总
| 反模式 | 修复 |
|---|---|
| 分散的重试逻辑 | 集中使用装饰器 |
| 硬编码配置 | 环境变量 + pydantic-settings |
| 暴露 ORM 模型 | DTO/响应架构 |
| I/O 与逻辑混杂 | 仓储模式 |
| 裸异常捕获 | 捕获特定异常 |
| 批处理遇错即停 | 返回包含成功和失败项的 BatchResult |
| 缺少验证 | 使用 Pydantic 在边界处验证 |
| 未关闭资源 | 上下文管理器 |
| 异步代码中执行阻塞操作 | 原生支持异步的库 |
| 缺少类型 | 为所有公共 API 添加类型注解 |
| 只测试正常路径 | 测试错误和边界情况 |
易踩坑
- 可变默认参数会在多次调用间共享状态:
def f(x=[]):— 每次调用时如果未提供x,都会访问同一个列表。使用x=None,并在函数内部创建列表。 - 裸写
except:或except Exception:会通过BaseException捕获KeyboardInterrupt— 使用裸except的长时间运行循环会悄无声息地拦截 Ctrl+C。 __slots__可以节省内存,但会破坏 pickling、weakref 和多重继承,除非在需要时准确声明__weakref__和__dict__条目。- 缓存的小整数/字符串应使用
==,而非is— 对小整数(-5..256)和驻留字符串,a is b会生效;其他情况下则可能不可预测地失效。比较值时始终使用==。 python -O会移除assert— 绝不要将其用于运行时验证;它只适用于代码内部的不变量。
评论
支持 Markdown