【问题标题】:Creating a StringAutoEnum class with a clean and concise syntax for auto-values为自动值创建一个具有简洁语法的 StringAutoEnum 类
【发布时间】:2019-10-23 10:02:56
【问题描述】:

我正在尝试实现一个 StringAutoEnum 类,它可以将其成员值设置为成员名称的小写版本,而不必为每个单个值调用 auto(),因为这很冗长,我根本不喜欢它.

假设这个基础StringAutoEnum 类:

import aenum

class StringAutoEnum(aenum.Enum):
    def _generate_next_value_(name, start, count, last_values):
        return name.lower()

理想的情况是这样的:

class Animal(StringAutoEnum):
    DOG, CAT, SNAKE

以下为真:

Animal.DOG.value == "dog"
Animal.CAT.value == "cat"
Animal.SNAKE.value == "snake"

显然,上面的类定义甚至不是有效的python代码(除非已经提前定义了DOG、CAT和SNAKE),所以这样做(据我所知),超出了问题。

因此,我考虑过以下几种替代方案:

分配给多个变量:

class Animal(StringAutoEnum):
    DOG = CAT = SNAKE = auto()

可迭代解包:

class Animal(StringAutoEnum):
    DOG, CAT, SNAKE = auto()

重申一下,我不想这样做:

class Animal(StringAutoEnum):
    DOG, CAT, SNAKE = auto(), auto(), auto()

而且我也不想使用函数式 API。

分配给多个变量的问题似乎是 auto() 只为第一个变量名计算一次,因此 Enum 的所有 3 个成员都以名称“DOG”和值“dog”结束。我还没有找到解决方法。

可迭代解包的问题在于,我想不出任何方法可以提前知道赋值运算符左侧有多少变量。

这意味着虽然这将起作用并且完全符合我的要求:

class Auto(aenum.auto):
    def __iter__(self):
        return iter([Auto() for _ in range(3)])

class Animal(StringAutoEnum):
    DOG, CAT, SNAKE = Auto()

不幸的是,这不会:

class Animal(StringAutoEnum):
    DOG, CAT, SNAKE, EMU = Auto()

而且我想不出任何办法以这种方式解决它以分配给任意数量的成员。

谁能想到一个干净简洁的替代方法来解决这个问题?或者,有人可以提出一个可行的分配给多个变量/可迭代解包的可行实现吗?

提前致谢!

【问题讨论】:

    标签: python python-3.x enums


    【解决方案1】:

    由于您使用的是aenum,因此您可以选择AutoEnum——它完全包含了元类的魔力:

    from aenum import AutoEnum
    
    class StringAutoEnum(AutoEnum):
        #
        def _generate_next_value_(name, start, count, last_values):
            return name.lower()
    
    class Animal(StringAutoEnum):
        DOG, CAT, SNAKE
    
    --> list(Animal)
    [<Color.Dog: 'dog'>, <Color.CAT: 'cat'>, <Color.SNAKE: 'snake'>]
    

    如果这太神奇了,下一个最佳解决方案是对每个值使用auto()(或使用函数式 API)。

    【讨论】:

    • 哇。我不敢相信我不知道 AutoEnum 类。基本上是完美的!您描述的方法的唯一缺点是 linters 不喜欢它,因此您必须 #noqa 这条线。
    【解决方案2】:

    这可以通过标准库轻松完成:

    from enum import Enum
    
    class StringAutoEnum(str, Enum):
        @staticmethod
        def _generate_next_value_(name, *_):
            return name.lower()
    
    Animal = StringAutoEnum('Animal', 'DOG CAT SNAKE')
    
    >>> list(Animal)
    [<Animal.DOG: 'dog'>, <Animal.CAT: 'cat'>, <Animal.SNAKE: 'snake'>]
    

    新类的第二个参数也可以是可迭代的而不是字符串。

    使用str mixin 类型不是必需的,但可以让您直接将枚举值(在本例中为小写名称)作为字符串使用,而无需先转换它们。

    【讨论】:

    • 我在问题中提到我不想使用函数式 API。原因是代码自省引擎和 IDE 通常无法理解以这种方式声明的枚举,因此您最终会失去通过使用类语法声明 Enum 获得的自动完成和 linting 功能,这是使用枚举的一半原因而不仅仅是“魔术字符串”。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-11-16
    • 2021-10-11
    • 1970-01-01
    • 1970-01-01
    • 2017-04-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多