PEP 663 – 枚举 str(), repr(), 和 format() 行为标准化
- 作者:
- Ethan Furman <ethan at stoneleaf.us>
- 讨论至:
- Python-Dev 列表
- 状态:
- 已拒绝
- 类型:
- 信息性
- 创建日期:
- 2021年6月30日
- Python 版本:
- 3.11
- 发布历史:
- 2021年7月20日, 2021年11月2日
- 决议:
- Python-Dev 消息
摘要
更新各种枚举类型的 repr(), str(), 和 format(),以更好地匹配其预期用途。例如,IntEnum 的 str() 将会更改以匹配其 format(),而用户混合的 int-enum 的 format() 将会匹配其 str()。在所有情况下,枚举的 str() 和 format() 将是相同的(除非用户覆盖 format())。
添加一个全局枚举装饰器,该装饰器将更改被装饰枚举的 str() 和 repr()(以及 format()),使其成为一个有效的全局引用:例如 re.IGNORECASE 而不是 <RegexFlag.IGNORECASE: 2>。
动机
IntEnum 和 IntFlag 的 str() 不是其值会导致错误和额外的工作,当替换现有常量时。
枚举成员的 str() 和 format() 不同可能会造成混淆。
添加 StrEnum 及其要求 str() 为其 value,这与其他提供的枚举的 str 不一致。
Flag 成员的迭代,这直接影响它们的 repr(),充其量是笨拙的,最坏情况是错误的。
基本原理
枚举在标准库中越来越普遍;能够通过其 repr() 识别枚举成员,并且该 repr() 易于解析,是有用的,并且可以节省理解和调试代码的时间和精力。
然而,带有混合数据类型的枚举(IntEnum, IntFlag, 和新的 StrEnum)需要对它们正在替换的现有常量有更好的向后兼容性——具体来说,str(replacement_enum_member) == str(original_constant) 应该为真(对于 format() 也是如此)。
IntEnum, IntFlag, 和 StrEnum 应该尽可能地成为现有整数和字符串常量的直接替换。为了实现这一目标,每个枚举的 str() 输出应该是其固有的值;例如,如果 Color 是一个 IntEnum
>>> Color.RED
<Color.RED: 1>
>>> str(Color.RED)
'1'
>>> format(Color.RED)
'1'
请注意,format() 已经产生正确的输出,只有 str() 需要更新。
尽可能地,枚举成员的 str(), repr(), 和 format() 应该在整个标准库中标准化。然而,直到 Python 3.10,标准库中的几个枚举都有自定义的 str() 和/或 repr()。
Flag 的 repr() 目前包含别名,这是不应该的;修复它当然会在某些情况下改变其 repr()。
规范
枚举用法有三个大类
- 简单:
Enum或Flag在不混合数据类型的情况下创建了一个新的枚举类 - 直接替换:
IntEnum,IntFlag,StrEnum创建了一个新的枚举类,该类还继承自int或str并使用int.__str__或str.__str__ - 用户混合枚举和标志用户创建自己的整数、浮点数、字符串等枚举,而不是使用 enum.IntEnum 等
还有两种风格
- 普通:枚举成员保留在它们的类中,并被访问为
classname.membername,并且类名显示在其repr()和str()中(在适当的情况下) - 全局:枚举成员被复制到它们模块的全局命名空间中,并且它们的模块名显示在其
repr()和str()中(在适当的情况下)
一些示例枚举
# module: tools.py
class Hue(Enum): # or IntEnum
LIGHT = -1
NORMAL = 0
DARK = +1
class Color(Flag): # or IntFlag
RED = 1
GREEN = 2
BLUE = 4
class Grey(int, Enum): # or (int, Flag)
BLACK = 0
WHITE = 1
使用上述枚举,以下两个表显示了旧输出和新输出(空白单元格表示无变化)
| 风格 | 类别 | 枚举 repr() | 枚举 str() | 枚举 format() | |
| 普通 | 简单 | 3.10 | |||
| 新 | |||||
| 用户混合 | 3.10 | 1 | |||
| 新 | Grey.WHITE | ||||
| 整数直接替换 | 3.10 | Hue.LIGHT | |||
| 新 | -1 | ||||
| 全局 | 简单 | 3.10 | <Hue.LIGHT: -1> | Hue.LIGHT | Hue.LIGHT |
| 新 | tools.LIGHT | LIGHT | LIGHT | ||
| 用户混合 | 3.10 | <Grey.WHITE: 1 | Grey.WHITE | Grey.WHITE | |
| 新 | tools.WHITE | WHITE | WHITE | ||
| 整数直接替换 | 3.10 | <Hue.LIGHT: -1> | Hue.LIGHT | ||
| 新 | tools.LIGHT | -1 | |||
| 风格 | 类别 | 标志 repr() | 标志 str() | 标志 format() | |
| 普通 | 简单 | 3.10 | <Color.RED|GREEN: 3> | Color.RED|GREEN | Color.RED|GREEN |
| 新 | <Color(3): RED|GREEN> | Color.RED|Color.GREEN | Color.RED|Color.GREEN | ||
| 用户混合 | 3.10 | <Grey.WHITE: 1> | 1 | ||
| 新 | <Grey(1): WHITE> | Grey.WHITE | |||
| 整数直接替换 | 3.10 | <Color.RED|GREEN: 3> | Color.RED|GREEN | ||
| 新 | <Color(3): RED|GREEN> | 3 | |||
| 全局 | 简单 | 3.10 | <Color.RED|GREEN: 3> | Color.RED|GREEN | Color.RED|GREEN |
| 新 | tools.RED|tools.GREEN | RED|GREEN | RED|GREEN | ||
| 用户混合 | 3.10 | <Grey.WHITE: 1> | Grey.WHITE | 1 | |
| 新 | tools.WHITE | WHITE | WHITE | ||
| 整数直接替换 | 3.10 | <Color.RED|GREEN: 3> | Color.RED|GREEN | ||
| 新 | tools.RED|tools.GREEN | 3 | |||
这两张表显示了最终结果
| 风格 | 类别 | 枚举 repr() | 枚举 str() | 枚举 format() |
| 普通 | 简单 | <Hue.LIGHT: -1> | Hue.LIGHT | Hue.LIGHT |
| 用户混合 | <Grey.WHITE: 1> | Grey.WHITE | Grey.WHITE | |
| 整数直接替换 | <Hue.LIGHT: -1> | -1 | -1 | |
| 全局 | 简单 | tools.LIGHT | LIGHT | LIGHT |
| 用户混合 | tools.WHITE | WHITE | WHITE | |
| 整数直接替换 | tools.LIGHT | -1 | -1 |
| 风格 | 类别 | 标志 repr() | 标志 str() | 标志 format() |
| 普通 | 简单 | <Color(3): RED|GREEN> | Color.RED|Color.GREEN | Color.RED|Color.GREEN |
| 用户混合 | <Grey(1): WHITE> | Grey.WHITE | Grey.WHITE | |
| 整数直接替换 | <Color(3): RED|GREEN> | 3 | 3 | |
| 全局 | 简单 | tools.RED|tools.GREEN | RED|GREEN | RED|GREEN |
| 用户混合 | tools.WHITE | WHITE | WHITE | |
| 整数直接替换 | tools.RED|tools.GREEN | 3 | 3 |
可以看出,repr() 主要受成员是否全局的影响,而 str() 受全局或直接替换的影响,其中直接替换的优先级更高。此外,标志的基本 repr() 和 str() 已更改,因为旧风格存在缺陷。
向后兼容性
字符串化对象的向后兼容性在主要 Python 版本之间不保证,并且在软件使用枚举的 repr(), str(), 和 format() 输出进行测试、文档、数据结构和/或代码生成时,会出现向后兼容性中断。
枚举成员的常规用法不会改变:re.ASCII 仍然可以作为 re.ASCII 使用,并且仍然等于 256。
如果需要保留之前的输出,例如为了确保不同 Python 版本之间的兼容性,软件项目将需要创建自己的枚举基类并覆盖相应的方法。
请注意,通过更改直接替换类别的 str(),我们将实际防止未来的中断,当 IntEnum 等用于替换现有常量时。
版权
本文档置于公共领域或 CC0-1.0-Universal 许可证下,以更宽松者为准。
来源:https://github.com/python/peps/blob/main/peps/pep-0663.rst