英文:
How does python3.11's StrEnum's MRO work differently for str and repr?
问题 {#heading}
Python 3.11引入了StrEnum
和IntEnum
,它们分别继承自str
或int
,并且还继承自ReprEnum
,而ReprEnum
则继承自Enum
。
ReprEnum
的实现实际上是空的。
>>> print(inspect.getsource(ReprEnum))
class ReprEnum(Enum):
"""
Only changes the repr(), leaving str() and format() to the mixed-in type.
"""
如果我创建一个StrEnum
并检查其MRO,我可以看到str
排在第一位。
class Strings(StrEnum):
A = "a"
>>> Strings.mro
(<enum 'Strings'>, <enum 'StrEnum'>, <class 'str'>, <enum 'ReprEnum'>, <enum 'Enum'>, <class 'object'>)
str
和Enum
都定义了__str__
和__repr__
方法。
>>> str.__repr__
<slot wrapper '__repr__' of 'str' objects>
>>> str.__str__
<slot wrapper '__str__' of 'str' objects>
>>> Enum.__repr__
<function Enum.__repr__ at 0x7ffff69f72e0>
>>> Enum.__str__
<function Enum.__str__ at 0x7ffff69f7380>
那么,__repr__
如何从Enum
继承,而__str__
如何从str
继承呢?
英文:
Python3.11 introduced StrEnum
and IntEnum
which inherit str
or int
respectively, and also inherit ReprEnum
, which in turn inherits Enum
.
ReprEnum
's implementation is actually empty.
>>> print(inspect.getsource(ReprEnum))
class ReprEnum(Enum):
"""
Only changes the repr(), leaving str() and format() to the mixed-in type.
"""
If I create a StrEnum
and check the MRO, I can see that str
comes first.
class Strings(StrEnum):
A = "a"
>>> Strings.mro
(<enum 'Strings'>, <enum 'StrEnum'>, <class 'str'>, <enum 'ReprEnum'>, <enum 'Enum'>, <class 'object'>)
Both str
and Enum
define a __str__
and a __repr__
>>> str.__repr__
<slot wrapper '__repr__' of 'str' objects>
>>> str.__str__
<slot wrapper '__str__' of 'str' objects>
>>> Enum.__repr__
<function Enum.__repr__ at 0x7ffff69f72e0>
>>> Enum.__str__
<function Enum.__str__ at 0x7ffff69f7380>
How then does __repr__
get inherited from Enum
and __str__
get inherited from str
?
答案1 {#1}
得分: 3
`__repr__` 方法以正常方式继承自 `Enum`(通过 `StrEnum`)
&gt;&gt;&gt; Strings.__repr__ is StrEnum.__repr__ is Enum.__repr__
True
</code></pre>
<p>对于 <code>__str__</code> 方法,元类 <code>EnumType</code> 检查了 <code>ReprEnum</code> 的存在,并在类定义时将混入数据类型的 <code>str</code> 和 <code>format</code> 处理"提升"到类命名空间中 <a href="https://github.com/python/cpython/blob/v3.11.4/Lib/enum.py#L575-L592">这里</a>:</p>
<pre><code>class EnumType(type):
...
def __new__(metacls, cls, bases, classdict, *, boundary=None, _simple=False, **kwds):
...
# 同样,对于 ReprEnum 的特殊处理
if ReprEnum is not None and ReprEnum in bases:
if member_type is object:
raise TypeError(
'ReprEnum 子类必须与数据类型(即 int、str、float 等)混合使用'
)
if '__format__' not in classdict:
enum_class.__format__ = member_type.__format__
classdict['__format__'] = enum_class.__format__
if '__str__' not in classdict:
method = member_type.__str__
if method is object.__str__:
# 如果 member_type 没有定义 __str__,object.__str__ 将使用其 __repr__,
# 所以我们也将使用其 __repr__
method = member_type.__repr__
enum_class.__str__ = method
classdict['__str__'] = enum_class.__str__
...
</code></pre>
<p>现在 <code>Strings.__str__</code> 方法可以直接在类命名空间中找到,不需要遍历 MRO。</p>
<pre><code><details>
<summary>英文:</summary>
The `__repr__` method comes the normal way, inherited from `Enum` (via `StrEnum`)
</code></pre>
<br />
<p>>>> Strings.<strong>repr</strong> is StrEnum.<strong>repr</strong> is Enum.<strong>repr</strong><br />
True</p>
<pre><code>For the `__str__` method, the metaclass `EnumType` checks for the presence of `ReprEnum` and &quot;hoists up&quot; the `str` and `format` handling of the mixed-in data type into the class namespace at class definition time [here](https://github.com/python/cpython/blob/v3.11.4/Lib/enum.py#L575-L592):
</code></pre>
<br />
<p>class EnumType(type):</p>
<pre><code>...
def new(metacls, cls, bases, classdict, \*, boundary=None, _simple=False, \*\*kwds):
...
# Also, special handling for ReprEnum
if ReprEnum is not None and ReprEnum in bases:
if member_type is object:
raise TypeError(
&amp;#39;ReprEnum subclasses must be mixed with a data type (i.e.&amp;#39;
&amp;#39; int, str, float, etc.)&amp;#39;
)
if &amp;#39;__format__&amp;#39; not in classdict:
enum_class.__format__ = member_type.__format__
classdict[&amp;#39;__format__&amp;#39;] = enum_class.__format__
if &amp;#39;__str__&amp;#39; not in classdict:
method = member_type.__str__
if method is object.__str__:
# if member_type does not define __str__, object.__str__ will use
# its __repr__ instead, so we&amp;#39;ll also use its __repr__
method = member_type.__repr__
enum_class.__str__ = method
classdict[&amp;#39;__str__&amp;#39;] = enum_class.__str__
...
</code></pre>
<br />
<pre><code>Now that a `Strings.__str__` method may be found directly in the class namespace, the MRO needn&#39;t be traversed.
</details>
</code></pre>
<br />