我试图对IntEnum进行子类化,以将成员的值开始为某个值,然后为后续成员自动设置该值。这是我的课:

class Abc(IntEnum):
  def __init__(self, n=100):
    super().__init__()
    self._value_ = n + len(self.__class__.__members__)

  A = () # 100
  B = () # 101

Abc.A == Abc.B # expects False, but gets True


如上所示,成员之间的比较是不正确的。当打印出Abc.dict时,我注意到_value2member_map_看起来也不正确。

mappingproxy({'A': <Abc.A: 100>,
          'B': <Abc.B: 101>,
          '__doc__': 'An enumeration.',
          '__init__': <function __main__.Abc.__init__>,
          '__module__': '__main__',
          '__new__': <function enum.Enum.__new__>,
          '_generate_next_value_': <function enum.Enum._generate_next_value_>,
          '_member_map_': OrderedDict([('A', <Abc.A: 100>),
                       ('B', <Abc.B: 101>)]),
          '_member_names_': ['A', 'B'],
          '_member_type_': int,
          '_value2member_map_': {0: <Abc.B: 101>}})


请注意,“ _ value2member_map_”如何具有键0而不是预期值100和101。我必须在init函数中缺少某些内容,但是我无法弄清楚如何正确地实现我的预期。任何帮助表示赞赏。
谢谢。

最佳答案

首先,有一种更惯用且简单的方法来完成您似乎想做的事情:

class Abc(IntEnum):
    A = 100
    B = auto()


或者,无论如何您都将100和101作为注释,实时代码总是比注释更好:

class Abc(IntEnum):
    A = 100
    B = 101


您没有执行任何一项操作的事实向读者发出信号,表明您可能正在做一些更复杂的事情。据我所知,除了你不是,所以这是误导。

另外,您正在组合两种具有截然相反含义的模式:正如the docs所说,使用()习惯用法“向用户表示这些值并不重要”,但是使用IntEnum显然意味着数值这些枚举常数不仅很重要,而且还很重要。

不仅如此,用户还必须通读方法代码以找出那些重要的数值,而不是立即读取它们。



无论如何,如果您想使其正常工作,问题在于初始化后替换_value_并没有任何好处,实际上却没有。

您要覆盖的是__new__,而不是the auto-numbering example in the docs中的__init__

但是这里有两个区别(两者都与您使用IntEnum而不是Enum的事实有关):


您不能调用object.__new__,因为IntEnumint,并且object.__new__不能用于诸如int之类的内置类型的实例。您可以通过查看cls的mro动态地找到正确的基类,或者您也可以在此处硬编码int
您不需要在这里进行中间基类的工作。 (当然,如果要创建多个自动编号的IntEnum,则可能仍需要一个。)


所以:

class Abc(IntEnum):
    def __new__(cls, n=100):
        value = len(cls.__members__) + n
        obj = int.__new__(cls, value)
        obj._value_ = value
        return obj
    A = ()
    B = ()

关于python - IntEnum子类比较不正确,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/51486818/

10-11 00:12