常量与枚举管理规范
常量定义、枚举使用、魔法值治理
[Requirement] 常量必须使用 UPPER_SNAKE_CASE 命名,禁止在业务逻辑中直接硬编码字面量。
[Standard] 常量使用语言提供的不可变机制定义:Python 用模块级变量(约定大写),Java 用 static final,TypeScript 用 as const。
[Caution] 不要把”会随环境变化的值”(如数据库地址、API Key)当作常量,应走配置中心或环境变量。
示例:
# 正确
DEFAULT_PAGE_SIZE = 20
MAX_RETRY_COUNT = 3
HTTP_STATUS_OK = 200
def fetch_users(page: int = DEFAULT_PAGE_SIZE) -> list[User]:
...| 类型 | 说明 | 示例 |
|---|---|---|
| 业务常量 | 业务规则中的固定值 | ORDER_STATUS_PAID = 2 |
| 技术常量 | 框架/协议相关固定值 | HTTP_TIMEOUT = 30 |
| 魔法值替换 | 原本散落在代码中的字面量 | MAX_FILE_SIZE = 10 * 1024 * 1024 |
[Standard] 当一组值具有明确的固定集合且彼此互斥时,必须使用枚举而非字符串常量。
[Requirement] 状态机、类型标识、分类标签等场景优先使用枚举。
示例:
from enum import Enum
class OrderStatus(Enum):
PENDING = "pending"
PAID = "paid"
SHIPPED = "shipped"
CANCELLED = "cancelled"
def can_cancel(status: OrderStatus) -> bool:
return status in (OrderStatus.PENDING, OrderStatus.PAID)[Standard] 枚举值优先使用字符串(便于日志和调试),涉及存储或性能时使用整数。
[Caution] 枚举值一旦发布不应随意修改;如需变更,新增枚举项而非修改已有项的值。
[Suggestion] 为枚举添加业务语义方法,而不是在外部写 if/elif 链:
class OrderStatus(Enum):
PENDING = "pending"
PAID = "paid"
SHIPPED = "shipped"
@property
def can_cancel(self) -> bool:
return self in (OrderStatus.PENDING, OrderStatus.PAID)
@property
def display_name(self) -> str:
names = {"pending": "待支付", "paid": "已支付", "shipped": "已发货"}
return names[self.value]魔法值是指代码中直接出现的、缺乏语义说明的字面量(数字、字符串)。
[Requirement] 以下情况禁止出现魔法值:
- 业务状态码(如
status == 2) - 超时、重试、限制等配置值(如
timeout = 30) - 文件大小、数量限制等阈值
[Default] 0、1、-1、空字符串等极少数通用值可豁免,但建议加注释说明用途。
- 发现:代码扫描(如
ruff的PLR2004规则)找出所有魔法值。 - 提取:替换为命名常量或枚举。
- 归类:按业务领域放入对应常量模块。
示例:
# 错误:魔法值
if order.status == 2:
cancel(order)
# 正确:使用枚举
if order.status == OrderStatus.PAID:
cancel(order)# 错误:魔法值
if len(content) > 10485760:
raise TooLargeError()
# 正确:命名常量
MAX_CONTENT_SIZE = 10 * 1024 * 1024 # 10 MB
if len(content) > MAX_CONTENT_SIZE:
raise TooLargeError()[Standard] 常量和枚举必须集中管理,不允许在业务文件中零散定义。
src/ constants/ __init__.py common.py # 通用常量(HTTP 状态码、超时等) business.py # 业务常量 enums/ __init__.py order.py # 订单相关枚举 user.py # 用户相关枚举[Caution] 避免创建一个”万能常量文件”(如 constants.py 放所有东西),应按领域拆分。
[Suggestion] 跨服务共享的常量应抽取到独立的公共包,各服务通过依赖引入,避免复制。
| 反模式 | 示例 | 问题 |
|---|---|---|
| 魔法数字 | if status == 3 | 无法理解 3 的含义 |
| 魔法字符串 | if role == "admin" | 拼写错误不会被编译器发现 |
| 常量散落 | 每个文件都定义 MAX_SIZE = 1024 | 修改时容易遗漏 |
| 枚举当常量字典用 | STATUS_MAP = {1: "a", 2: "b"} | 没有类型安全,调用方可能传入非法值 |
| 硬编码配置 | db.connect("localhost", 3306) | 环境切换需改代码 |
| 枚举值随意变更 | 把 PAID = 2 改成 PAID = 3 | 历史数据与新枚举值不兼容 |