Python 3.11中StrEnum的MRO对__str__与__repr__的继承机制差异问询
这个问题问得特别到位,咱们一步步拆解来搞明白背后的逻辑:
首先先回顾下背景:Python 3.11新增了StrEnum和IntEnum,它们分别继承自str/int以及ReprEnum,而ReprEnum本身又是Enum的子类。有意思的是,ReprEnum的实现其实是空的,只有一段说明它作用的文档字符串:
import inspect from enum import ReprEnum print(inspect.getsource(ReprEnum))
输出结果是:
class ReprEnum(Enum):
"""
Only changes the repr(), leaving str() and format() to the mixed-in type.
"""
接下来看你定义的StrEnum子类的方法解析顺序(MRO):
from enum import StrEnum class Strings(StrEnum): A = "a" print(Strings.__mro__)
得到的MRO顺序是:
(<enum 'Strings'>, <enum 'StrEnum'>, <class 'str'>, <enum 'ReprEnum'>, <enum 'Enum'>, <class 'object'>)
你也注意到了,str和Enum都各自定义了__str__和__repr__方法:
# 查看str类的方法 print(str.__repr__) # <slot wrapper '__repr__' of 'str' objects> print(str.__str__) # <slot wrapper '__str__' of 'str' objects> # 查看Enum类的方法 from enum import Enum print(Enum.__repr__) # <function Enum.__repr__ at 0x7ffff69f72e0> print(Enum.__str__) # <function Enum.__str__ at 0x7ffff69f7380>
那为什么明明str在MRO里的位置比Enum靠前,__repr__却继承自Enum,而__str__继承自str呢?
其实核心就藏在ReprEnum的文档字符串里——它的设计意图就是只修改repr()的行为,把str()和format()的逻辑完全交给混合的原生类型(这里就是str)。而实现这个逻辑的关键,是枚举的元类EnumMeta在背后做了特殊处理:
对于
__str__:按照ReprEnum的设计,直接沿用混合类型str的实现。因为MRO里str的位置确实更靠前,元类也没有干预这个方法的查找,所以调用str(Strings.A)时,会返回"a",和普通字符串的__str__效果一致。对于
__repr__:元类会根据ReprEnum的标记,调整方法查找的优先级,跳过str的__repr__,转而使用Enum的实现。这样调用repr(Strings.A)时,会返回<Strings.A: 'a'>,保留枚举成员的标识信息,符合枚举类型的调试需求。
简单来说,ReprEnum就是个标记类,它本身不需要写代码,只要告诉EnumMeta:这个混合类型枚举的repr()用枚举的逻辑,str()用原生类型的逻辑就行。
备注:内容来源于stack exchange,提问作者Abraham Murciano Benzadon

