【问题标题】:Create property for Enum class in Python在 Python 中为 Enum 类创建属性
【发布时间】:2019-09-04 21:14:10
【问题描述】:

我必须在 Enum 中创建一个额外的映射来保存额外的信息,例如每个枚举案例的详细描述,但不会丢失 Enum 类属性。例如:

class MyEnumBase:
    description = {
        1: 'Description 1',
        2: 'This is anover description',
        3: 'la la la',
    }

class MyEnum(MyEnumBase, Enum):
    First = 1
    Second = 2
    Third = 3

所以我这样访问它: MyEnum.description[3] == 'la la la'.

如何扩展 Enum 类,以便在描述与枚举名称相同的情况下,它会使用字段名称填充此字典?

例如:

class MyAnotherEnum(CustomEnumBase):
    aaa = 1
    bbb = 2
    ccc = 3

这样MyAnotherEnum.description[3] == 'ccc' 会自动为从此CustomEnumBase 创建的每个枚举生成description 属性。

我尝试扩展 Enum 类,但我尝试过的所有事情都失败了。我在想这样的事情:

class CustomEnumBase:
    @property
    def names(cls):
        return {
            id:member.name
            for id, member in cls._value2member_map_.items()
        }

有两个限制:

  1. 它应该扩展 python Enum 类,因此期望 Enum 的地方它的行为正确。有一些第三方代码依赖于此。

  2. 它应该保持对描述的访问作为映射,因为这个协议已经被预期并在代码库中使用。因此将其更改为MyAnotherEnum.description()[id] 是不可行的,因为它需要一个映射(dict),而不是一个函数。

【问题讨论】:

  • 为什么首先需要使用description dict? MyAnotherEnum(3).value 将返回 ccc,而 MyAnotherEnum['ccc'] 返回 3
  • 可以更多地解释您希望通过枚举类实现的目标。正如 chen 所说,您的用例似乎不需要任何枚举扩展
  • 我的枚举具有 id(数字)和枚举名称。有时,我需要对此枚举进行详细描述。对于这种情况,我发现有一种方法可以在不丢失 Enum 协议/api 的情况下对其进行封装。
  • 重点是有很多新的情况,描述名称与名称相同,所以不是在每个新的 Enum 中创建这种重复的代码,而是创建一种以编程方式使描述为枚举本身的名称。并且在需要时,在描述不同时手动覆盖描述。

标签: python python-3.x class enums


【解决方案1】:

如果您使用 Python 3.6+,stdlib Enum 可以;否则,您将需要使用 aenum1 库。在这两种情况下,您都需要创建自己的 _generate_next_value_ 方法:

def _generate_next_value_(name, start, count, last_values):
    return start+count, name
  • name 是成员的名字
  • start 是枚举的起始值(默认为 1)
  • count 是到目前为止的成员数
  • last_values 是目前为止的成员值列表

this answerclassproperty

class classproperty:

    def __init__(self, func):
        self._func = func

    def __get__(self, obj, owner):
        return self._func(owner)

如果使用aenum(我最喜欢的),基类将如下所示:

from aenum import Enum, auto

class DescriptionEnum(Enum):
    _init_ = 'value description'

    @staticmethod
    def _generate_next_value_(name, start, count, last_values):
        return start+count, name

    @classproperty
    def description(cls):
        return {
            e.value: e.description
            for e in cls
            }
    print(description)

如果使用 Python 3.6 和 stdlib Enum,基类将如下所示:

from enum import Enum, auto

class DescriptionEnum(Enum):

    def __new__(cls, value, description):
        obj = object.__new__(cls)
        obj._value_ = value
        obj.description = description
        return obj

    def _generate_next_value_(name, start, count, last_values):
        print('generating value', start+count, name)
        return start+count, name

    @classproperty
    def description(cls):
        return {
            e.value: e.description
            for e in cls
            }

这两个基类之间的区别在于_init_ 行与__new__ 方法。在这两种情况下,你的最后一堂课都是:

class MyAnotherEnum(DescriptionEnum):
    aaa = auto()
    bbb = auto()
    ccc = auto()

并在使用中:

>>> print(list(MyAnotherEnum))
    [<MyAnotherEnum.aaa: 1>, <MyAnotherEnum.bbb: 2>, <MyAnotherEnum.ccc: 3>]

>>> print(MyAnotherEnum.bbb)
    MyAnotherEnum.bbb

>>> print(MyAnotherEnum.bbb.description)
    bbb

>>> print(MyAnotherEnum.description)
    {1: 'aaa', 2: 'bbb', 3: 'ccc'}

>>> print(MyAnotherEnum.description[2])
    bbb

虽然您的Enums 没有它,但这个新的description 为每个成员以及整个Enum 提供了一个属性。这样做的一个好处是您可以对名称为描述的枚举和指定描述的枚举使用相同的基类:

class MyNormalEnum(DescriptionEnum):
    aaa = 1, 'howdy'
    bbb = 2, 'bye'
    ccc = 3, 'whatever'

并在使用中:

>>> print(list(MyNormalEnum))
    [<MyNormalEnum.aaa: 1>, <MyNormalEnum.bbb: 2>, <MyNormalEnum.ccc: 3>]

>>> print(MyNormalEnum.bbb)
    MyNormalEnum.bbb

>>> print(MyNormalEnum.bbb.description)
    bye

>>> print(MyNormalEnum.description)
    {1: 'howdy', 2: 'bye', 3: 'whatever'}

>>> print(MyNormalEnum.description[2])
    bye

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-07
    • 2022-01-14
    • 1970-01-01
    • 1970-01-01
    • 2011-01-11
    相关资源
    最近更新 更多