【问题标题】:Modify enum.IntFlag __repr__修改 enum.IntFlag __repr__
【发布时间】:2021-03-03 20:32:45
【问题描述】:

我编写了一个小实用程序来解码由位标志组成的日志输出。我在解码这些位标志时使用枚举。

python --version
Python 3.8.6

为简洁起见,我只想打印枚举成员名称。我已将我的问题提炼为这个简短的 sn-p:

from enum import IntFlag
class Foo(IntFlag):
    A = 0x01
    B = 0x02
    C = 0x04
    def __str__(self):
        return self.name
    __repr__ = __str__

如果我将它们单独显示或将它们作为列表的一部分打印,则效果很好。但我无法打印 or 表达式。我想在A|B|C 表单上显示标志。

>>> Foo.A
A
>>> Foo.B
B
>>> [Foo.A, Foo.B]
[A, C]
>>> Foo.A | Foo.B
TypeError: __repr__ returned non-string (type NoneType)

更新:我查看了错误的源代码版本。 3.8.6 标签的IntFlag::__repr__ 确实包含打印每个成员https://github.com/python/cpython/blob/v3.8.6/Lib/enum.py#L761 的代码

我尝试阅读 CPythons Lib/enum.py,但无法弄清楚默认 IntFlag(没有我覆盖的 __repr__)如何在 Foo.A|Foo.B 表单上打印输出。~~

我可以看到IntFlag.__or__ 通过调用__class__ 返回了一个新对象(我真的不知道__class__ 做了什么)。而Enum.__repr__ 只打印一个类名和枚举名。

如何编写一个 __repr__ 函数和一个返回当前设置的枚举成员名称的字符串表示形式的 __str__ 函数?

【问题讨论】:

  • 您正在阅读错误版本的源代码 - 您正在寻找的 __repr__ 行为最近在 master 分支中被删除。 (始终查看您实际使用的版本的代码!)
  • enum 有太多奇怪的魔法。
  • @user2357112supportsMonica 啊,你是对的:github.com/python/cpython/blob/v3.8.6/Lib/enum.py#L761 有一个循环打印枚举成员。我同意你的观点,enum.py 内部发生了很多事情。

标签: python enums


【解决方案1】:
from enum import Flag, _power_of_two

class BetterFlag(Flag):
    def __repr__(self):
        if self.value == 0:
            return "%s(0)" % self.__class__.__name__
        return '|'.join(
                m.name
                for m in self.__class__
                if m.value & self.value and _power_of_two(m.value)
                )
    __str__ = __repr__

Python 3.10 及更早版本通过测试name 属性是否为None 来执行此操作,如果是则循环遍历内容。对于更奇特的枚举,循环存在缺陷,已在 3.11 中重做。

注意:以上是向前和向后兼容的。让您的 Flags 继承自 BetterFlag

【讨论】:

  • class 成员使得编写带有 repr 和 str impl 的 StrMixin 成为可能。对于想要同时修改 IntEnum 和 IntFlag 的我很有用。
  • _power_of_two 真的是公共 api 吗?它以下划线开头。按照惯例,这些函数不是私有的吗?
  • 它不是一个公共 API,但它可以完成这项工作。您可以 1) 将其复制到您自己的代码中; 2)添加一个测试,以便您知道它何时中断; 3) 在不再需要时删除BetterFlag 解决方法。我建议(1)或(2)(3)。
【解决方案2】:

我想出了这个解决方案

class Foo(IntFlag):
    A = 0x01
    B = 0x02
    C = 0x04
    def __repr__(self):
        return '|'.join(val.name for val in Foo if self.value & val)

以及repl中的一个演示:

>>> Foo.A | Foo.C | Foo.B
A|B|C
>>> Foo.A
A

【讨论】:

  • 只要您的Flags 简单,您的解决方案就可以正常工作。如果您在Flag 类定义中命名了组合,例如AB = 0x03BC = 0x06,它将失败。
猜你喜欢
  • 2023-01-05
  • 2020-08-27
  • 1970-01-01
  • 2018-06-01
  • 2021-05-05
  • 2012-10-04
  • 2011-12-07
  • 2016-09-23
  • 2020-09-13
相关资源
最近更新 更多