PEP 663 – 标准化 Enum 的 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 消息
摘要
更新各种 Enum 类型的 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
与其他提供的 Enum 的 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 | ||||
int 直接替换 | 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 | ||
int 直接替换 | 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 | |||
int 直接替换 | 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 | ||
int 直接替换 | 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 | |
int 直接替换 | <Hue.LIGHT: -1> | -1 | -1 | |
全局 | 简单 | tools.LIGHT | LIGHT | LIGHT |
用户混合 | tools.WHITE | WHITE | WHITE | |
int 直接替换 | 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 | |
int 直接替换 | <Color(3): RED|GREEN> | 3 | 3 | |
全局 | 简单 | tools.RED|tools.GREEN | RED|GREEN | RED|GREEN |
用户混合 | tools.WHITE | WHITE | WHITE | |
int 直接替换 | 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-通用许可证,以较宽松者为准。
来源:https://github.com/python/peps/blob/main/peps/pep-0663.rst
上次修改时间:2023年9月9日 17:39:29 GMT