【问题标题】:How to use enum name as value with other information如何将枚举名称用作其他信息的值
【发布时间】:2018-03-14 18:40:40
【问题描述】:

我对此进行了研究并找到了类似的答案,但它们在这种情况下不起作用,因为 auto() 的解析早于 __new__ 被调用,并且只有当整个值为 _auto_null 时。

基本上,我想要的是以下内容:

class MyEnum(str, Enum):
    one = '1 data'
    two = '2 data'

    def __new__(cls, data):
        member = str.__new__(cls, <NEED NAME HERE>)
        member.data = data
        member._value_ = <NEED NAME HERE>

assert MyEnum.one == 'one'
assert MyEnum.one.value == 'one'
assert MyEnum.one.data == '1 data'

但是,名称没有传递到__new__,所以没有什么可以填写&lt;NEED NAME HERE&gt;

然后,我尝试通过以下方式使用auto()

class MyEnumBase(Enum):
    def _generate_next_value_(name, start, count, last_values):
        return name

class MyEnum(str, MyEnumBase):
    one = '1 data'
    two = '2 data'

    def __new__(cls, data):
        member = str.__new__(cls, auto())
        member.data = data
        member._value_ = auto()

assert MyEnum.one == 'one'
assert MyEnum.one.value == 'one'
assert MyEnum.one.data == '1 data'

这不起作用,因为auto() 仅在定义成员时用作唯一值时才被解析,如one = auto()。因此,似乎甚至不可能执行 one = auto(), '1 data' 然后在 __new__ 函数中获取这两个参数。

class MyEnum(str, MyEnumBase):
    one = auto(), '1 data'
    two = auto(), '2 data'

    def __new__(cls, name, data):
        member = str.__new__(cls, name)
        member.data = data
        member._value_ = name

看来我唯一的选择是硬编码名称:

one = 'one', '1 data'

并定义__new__ 函数以获取两个参数:

def __new__(cls, name, data):
    ...

我错过了什么吗?有一个更好的方法吗?构建此信息的更好方法?

【问题讨论】:

  • 多重继承,枚举元类,天哪!我相信 Ethan Furman 很快就会和你在一起。
  • @wim:好吧,也许需要几分钟——我不得不去查看许多测试,看看我想给出哪个答案。 ;-)

标签: python enums python-3.6


【解决方案1】:

这绝对是高级行为,所以要使用它,您需要 aenum1 库。

该代码将如下所示:

from aenum import Enum

class MyEnum(str, Enum, init='value data'):

    def __new__(cls, name, *args, **kwds):
        obj = str.__new__(cls, name)
        obj._value_ = name
        return obj

    def _generate_next_value_(name, start, count, last_values, *args, **kwds):
        return (name, ) + args

    one = '1 data'
    two = '2 data'

init 设置说明了给定值的用途;在这种情况下:

  • 每个成员的value
  • 每个成员的data 属性

此外,如果提供的项目少于init 所说的应该有,则将调用_generate_next_value_ 以尝试提供缺失的部分。

在这种情况下,_generate_next_valuename 添加到给定的data,然后将其传递给__new__

__new__ 使用 name 作为它的值并忽略其余部分

__init__是自动生成来处理非值项的,所以它设置了data属性


对于那些仍在使用 Python 2.7,或者需要他们的代码在 2.7 和 3.x 上工作的人,上面的类应该如下所示(所有更改都在第一个块中):

class MyEnum(str, Enum):
    _order_ = 'one two'          # only if order actually matters
    _settings_ = AutoValue
    _init_ = 'value data'

    def __new__(cls, name, *args, **kwds):
        obj = str.__new__(cls, name)
        obj._value_ = name
        return obj

    def _generate_next_value_(name, start, count, last_values, *args, **kwds):
        return (name, ) + args

    one = '1 data'
    two = '2 data'

1 披露:我是Python stdlib Enumenum34 backportAdvanced Enumeration (aenum) 库的作者。

【讨论】:

  • 谢谢!太棒了。有一些问题,但我更仔细地重读了你的答案,我想我现在明白了。谢谢这么快的回复!
  • 这个例子中的另一个问题。由于我是 str 的子类,因此我希望我的对象在我想要的时候始终表现为 str,即我从不希望调用 Enum str 方法。所以我写了__str__ = lambda self: self。因为 self 实际上是 str 的一个实例。这恰好适用于上述示例,但当我添加第二个“数据”字段时停止工作。调试库后,我发现 Enum.__init__ 被调用,并以 str 的结果作为它的参数......这是为什么?返回 self.name 得到我想要的行为,但为什么要调用任何 init
  • @W.B.Reed:如果指定了_init_,则将调用__init__。我可以通过一个确切的例子来更好地回答问题的第一部分,说明您正在使用“第二个数据字段”做什么(请作为一个新问题)。至于原始问题,当 Enum 覆盖它时,获得混合类行为的正确方法是将其放回原处。 IE。 __str__ = str.__str__.
  • 谢谢@EthanFurman。我在stackoverflow.com/questions/49518098/… 提出了一个新问题。对于任何有兴趣的人。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-23
  • 2015-08-02
  • 1970-01-01
相关资源
最近更新 更多