本文详解如何在自定义 `enum` 子类中正确实现自动递增整数枚举值(类似 `auto()` 行为),同时为每个成员附加额外属性(如正则规则字符串),解决直接在 `__new__` 中调用 `auto()` 失败的问题。
在 Python 的 enum 模块中,auto() 是一个占位符对象,它仅在枚举类定义阶段被 EnumMeta 元类解析并替换为实际值,而非运行时可调用函数。因此,在 __new__ 方法内部直接写 auto()(如 member._value_ = auto())是无效的——此时 auto() 尚未被求值,且 __new__ 已处于成员实例化阶段,元类干预早已完成。
要实现“声明式赋值字符串、自动分配唯一整数 _value_、并保留原始字符串为自定义属性”的目标,核心思路是:手动模拟 auto() 的递增逻辑,并确保使用正确的基类构造方式。以下是推荐的完整实现方案:
✅ 正确做法:基于 IntEnum 的自动整数分配(推荐)
from enum import IntEnum
class TokenEnum(IntEnum):
def __new__(cls, value):
# 手动计算下一个整数值:从 1 开始递增(与 auto() 默认行为一致)
int_value = len(cls.__members__) + 1
# 关键:必须使用 int.__new__(cls, int_value) 构造整数实例
member = int.__new__(cls, int_value)
member._value_ = int_value # 显式赋值(冗余但清晰)
member.rule = value # 保存原始字符串(如正则表达式)
return member
# 使用示例
class Tokens(TokenEnum):
ID = r'[a-zA-Z_][a-zA-Z0-9_]*'
NUMBER = r'\d+'
PLUS = r'\+'
# 验证结果
print(Tokens.ID) # Tokens.ID
print(Tokens.ID.value) # 1
print(Tokens.ID.rule) # '[a-zA-Z_][a-zA-Z0-9_]*'
print(Tokens.NUMBER.value) # 2
⚠️ 注意事项:必须继承 IntEnum(而非 Enum 或 object),否则无法支持整数运算和比较;构造实例时务必调用 int.__new__(cls, int_value),而非 object.__new__(cls),否则会丢失 int 类型特性;len(cls.__members__) 在 __new__ 调用时已包含当前正在创建的成员?不,它只包含此前已成功创建的成员,因此 +1 是安全且准确的起始偏移。
? 替代方案:非整数枚举但 _value_ 仍为 int(兼容性更强)
若你不需要枚举成员本身是 int 实例(例如不参与算术运算),但希望 .value 返回整数、.name 和 .rule 保持分离,可改用 Enum 基类:
from enum import Enum
class TokenEnum(Enum):
def __new__(cls, value):
int_value = len(cls.__members__) + 1
member = object.__new__(cls) # 不依赖 int 构造
member._value_ = int_value # 但 _value_ 仍是 int
member.rule = value
return member
此时 Tokens.ID 是 TokenEnum 实例,Tokens.ID.value 为 1,Tokens.ID.rule 为正则字符串,语义更清晰,也避免了 IntEnum 的隐式类型约束。
立即学习“Python免费学习笔记(深入)”;