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

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

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

问题

Python 3.11引入了StrEnumIntEnum,它们分别继承自strint,并且还继承自ReprEnum,而ReprEnum则继承自Enum

ReprEnum的实现实际上是空的。

  1. >>> print(inspect.getsource(ReprEnum))
  2. class ReprEnum(Enum):
  3. """
  4. Only changes the repr(), leaving str() and format() to the mixed-in type.
  5. """

如果我创建一个StrEnum并检查其MRO,我可以看到str排在第一位。

  1. class Strings(StrEnum):
  2. A = "a"
  1. >>> Strings.__mro__
  2. (<enum 'Strings'>, <enum 'StrEnum'>, <class 'str'>, <enum 'ReprEnum'>, <enum 'Enum'>, <class 'object'>)

strEnum都定义了__str____repr__方法。

  1. >>> str.__repr__
  2. <slot wrapper '__repr__' of 'str' objects>
  3. >>> str.__str__
  4. <slot wrapper '__str__' of 'str' objects>
  5. >>> Enum.__repr__
  6. <function Enum.__repr__ at 0x7ffff69f72e0>
  7. >>> Enum.__str__
  8. <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.

  1. >>> print(inspect.getsource(ReprEnum))
  2. class ReprEnum(Enum):
  3. """
  4. Only changes the repr(), leaving str() and format() to the mixed-in type.
  5. """

If I create a StrEnum and check the MRO, I can see that str comes first.

  1. class Strings(StrEnum):
  2. A = "a"
  1. >>> Strings.__mro__
  2. (<enum 'Strings'>, <enum 'StrEnum'>, <class 'str'>, <enum 'ReprEnum'>, <enum 'Enum'>, <class 'object'>)

Both str and Enum define a __str__ and a __repr__

  1. >>> str.__repr__
  2. <slot wrapper '__repr__' of 'str' objects>
  3. >>> str.__str__
  4. <slot wrapper '__str__' of 'str' objects>
  5. >>> Enum.__repr__
  6. <function Enum.__repr__ at 0x7ffff69f72e0>
  7. >>> Enum.__str__
  8. <function Enum.__str__ at 0x7ffff69f7380>

How then does __repr__ get inherited from Enum and __str__ get inherited from str?

答案1

得分: 3

  1. `__repr__` 方法以正常方式继承自 `Enum`通过 `StrEnum`
  2. ```python
  3. >>> Strings.__repr__ is StrEnum.__repr__ is Enum.__repr__
  4. True

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

  1. class EnumType(type):
  2. ...
  3. def __new__(metacls, cls, bases, classdict, *, boundary=None, _simple=False, **kwds):
  4. ...
  5. # 同样,对于 ReprEnum 的特殊处理
  6. if ReprEnum is not None and ReprEnum in bases:
  7. if member_type is object:
  8. raise TypeError(
  9. 'ReprEnum 子类必须与数据类型(即 int、str、float 等)混合使用'
  10. )
  11. if '__format__' not in classdict:
  12. enum_class.__format__ = member_type.__format__
  13. classdict['__format__'] = enum_class.__format__
  14. if '__str__' not in classdict:
  15. method = member_type.__str__
  16. if method is object.__str__:
  17. # 如果 member_type 没有定义 __str__,object.__str__ 将使用其 __repr__,
  18. # 所以我们也将使用其 __repr__
  19. method = member_type.__repr__
  20. enum_class.__str__ = method
  21. classdict['__str__'] = enum_class.__str__
  22. ...

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

  1. <details>
  2. <summary>英文:</summary>
  3. The `__repr__` method comes the normal way, inherited from `Enum` (via `StrEnum`)

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

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

  1. ...
  2. def __new__(metacls, cls, bases, classdict, *, boundary=None, _simple=False, **kwds):
  3. ...
  4. # Also, special handling for ReprEnum
  5. if ReprEnum is not None and ReprEnum in bases:
  6. if member_type is object:
  7. raise TypeError(
  8. &#39;ReprEnum subclasses must be mixed with a data type (i.e.&#39;
  9. &#39; int, str, float, etc.)&#39;
  10. )
  11. if &#39;__format__&#39; not in classdict:
  12. enum_class.__format__ = member_type.__format__
  13. classdict[&#39;__format__&#39;] = enum_class.__format__
  14. if &#39;__str__&#39; not in classdict:
  15. method = member_type.__str__
  16. if method is object.__str__:
  17. # if member_type does not define __str__, object.__str__ will use
  18. # its __repr__ instead, so we&#39;ll also use its __repr__
  19. method = member_type.__repr__
  20. enum_class.__str__ = method
  21. classdict[&#39;__str__&#39;] = enum_class.__str__
  22. ...
  1. Now that a `Strings.__str__` method may be found directly in the class namespace, the MRO needn&#39;t be traversed.
  2. </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:

确定