python3.11的StrEnum的MRO在__str__和__repr__方面有何不同?

huangapple go评论105阅读模式
英文:

How does python3.11's StrEnum's MRO work differently for __str__ and __repr__?

问题

Python 3.11引入了StrEnumIntEnum,它们分别继承自strint,并且还继承自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'>)

strEnum都定义了__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

得分: 3

`__repr__` 方法以正常方式继承自 `Enum`(通过 `StrEnum`)

```python
>>> Strings.__repr__ is StrEnum.__repr__ is Enum.__repr__
True

对于 __str__ 方法,元类 EnumType 检查了 ReprEnum 的存在,并在类定义时将混入数据类型的 strformat 处理“提升”到类命名空间中 这里

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__

        ...

现在 Strings.__str__ 方法可以直接在类命名空间中找到,不需要遍历 MRO。


<details>
<summary>英文:</summary>

The `__repr__` method comes the normal way, inherited from `Enum` (via `StrEnum`)

>>> Strings.repr is StrEnum.repr is Enum.repr
True


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):

class EnumType(type):

...

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(
                    &#39;ReprEnum subclasses must be mixed with a data type (i.e.&#39;
                    &#39; int, str, float, etc.)&#39;
                    )
        if &#39;__format__&#39; not in classdict:
            enum_class.__format__ = member_type.__format__
            classdict[&#39;__format__&#39;] = enum_class.__format__
        if &#39;__str__&#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&#39;ll also use its __repr__
                method = member_type.__repr__
            enum_class.__str__ = method
            classdict[&#39;__str__&#39;] = enum_class.__str__

    ...

Now that a `Strings.__str__` method may be found directly in the class namespace, the MRO needn&#39;t be traversed.

</details>



huangapple
  • 本文由 发表于 2023年8月10日 23:07:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/76877041.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定