Python 中基于字符串的枚举

sop*_*ros 12 python string enums python-3.x

要封装我使用enum模块的状态列表:

from enum import Enum

class MyEnum(Enum):
    state1='state1'
    state2 = 'state2'

state = MyEnum.state1
MyEnum['state1'] == state  # here it works
'state1' == state  # here it does not throw but returns False (fail!)
Run Code Online (Sandbox Code Playgroud)

但是,问题是我需要在脚本的许多上下文中无缝地将值用作字符串,例如:

select_query1 = select(...).where(Process.status == str(MyEnum.state1))  # works but ugly

select_query2 = select(...).where(Process.status == MyEnum.state1)  # throws exeption
Run Code Online (Sandbox Code Playgroud)

如何避免调用额外的类型转换(str(state)以上)或底层值(state.value)?

Ely*_*755 122

通过阅读文档(即,我没有尝试它,因为我使用旧版本的 Python,但我信任文档),从Python 3.11开始,您可以执行以下操作:

\n
from enum import StrEnum\n\nclass Directions(StrEnum):\n    NORTH = \'north\'\n    SOUTH = \'south\'\n\nprint(Directions.NORTH)\n>>> north\n
Run Code Online (Sandbox Code Playgroud)\n

请注意,看起来在子类化 StrEnum 时,将枚举字段定义为单值元组根本没有区别,并且也将被视为字符串,如下所示:

\n
class Directions(StrEnum):\n    NORTH = \'north\',    # notice the trailing comma\n    SOUTH = \'south\'\n
Run Code Online (Sandbox Code Playgroud)\n

请参阅文档设计讨论以进一步了解。

\n

如果您运行的是python 3.6+,请执行pip install StrEnum,然后您可以执行以下操作(经我确认):

\n
from strenum import StrEnum\n\nclass URLs(StrEnum):\n    GOOGLE = \'www.google.com\'\n    STACKOVERFLOW = \'www.stackoverflow.com\'\n\nprint(URLs.STACKOVERFLOW)\n\n>>> www.stackoverflow.com\n
Run Code Online (Sandbox Code Playgroud)\n

你可以在这里读更多关于它的内容。

\n
\n此外,文档中提到了这一点 - 如何基于其他类创建自己的枚举:\n
\n

虽然 IntEnum 是 enum 模块的一部分,但独立实现\n会非常简单:

\n

class IntEnum(int, Enum):\npass 这演示了如何定义类似的派生枚举;例如,混合了 str 而不是 int 的 StrEnum。

\n

一些规则:

\n

当对 Enum 进行子类化时,混合类型必须在基数序列中出现在 Enum 本身之前,如上面的 IntEnum 示例所示。

\n

虽然 Enum 可以具有任何类型的成员,但一旦混合了其他\n类型,所有成员都必须具有该类型的值,例如上面的 int。\n此限制不适用于仅添加方法和\ndon\ 的混合。 xe2\x80\x99t 指定另一种类型。

\n

当混合另一种数据类型时,值属性与枚举成员本身不同,尽管它是等效的并且比较相等。

\n

%式格式:%s和%r分别调用Enum类\xe2\x80\x99s str ()和\n repr ();其他代码(例如 IntEnum 的 %i 或 %h)将枚举成员视为其混合类型。

\n

格式化字符串文字、str.format() 和 format() 将使用混合类型\xe2\x80\x99s format (),除非在子类中重写了str () 或format (),在这种情况下将使用重写的方法或\nEnum 方法。使用 !s 和 !r 格式代码强制使用 Enum 类\xe2\x80\x99s str () 和repr () 方法。

\n
\n

来源: https: //docs.python.org/3/library/enum.html#others

\n

  • StrEnum 的值为 3.11 (4认同)
  • 让我们在序列化中使用自动生成的枚举值。然后,通过更新到有缺陷的“strenum”版本,它可以很容易地被破坏。这不值得。 (2认同)
  • 这应该是 python>=3.11 的正确答案 (2认同)

sop*_*ros 27

似乎同时从str类继承就足够了Enum

class MyEnum(str, Enum):
    state1='state1'
    state2 = 'state2'
Run Code Online (Sandbox Code Playgroud)

棘手的部分是继承链中类的顺序很重要,因为:

class MyEnum(Enum, str):
    state1='state1'
    state2 = 'state2'
Run Code Online (Sandbox Code Playgroud)

抛出:

TypeError: new enumerations should be created as `EnumName([mixin_type, ...] [data_type,] enum_type)`
Run Code Online (Sandbox Code Playgroud)

使用正确的类,以下操作就MyEnum可以了:

print('This is the state value: ' + state)
Run Code Online (Sandbox Code Playgroud)

作为旁注,似乎格式化字符串不需要特殊的继承技巧,即使Enum仅适用于继承:

msg = f'This is the state value: {state}'  # works without inheriting from str
Run Code Online (Sandbox Code Playgroud)

  • 如果您使用的是 Python 3.11+,那么将 `StrEnum` 子类化会更简单 - https://docs.python.org/3/howto/enum.html#strenum。 (4认同)
  • 相关文档:https://docs.python.org/3/library/enum.html#restricted-enum-subclassing (2认同)
  • 请注意,aa `str` mixin 可能会产生意想不到的副作用,例如对(反)序列化行为;例如参见/sf/ask/4573774481/ (2认同)

小智 20

使用该值有什么问题?

恕我直言,除非将 Python 版本 3.11 与 StrEnum 一起使用,否则我只需重写__str__(self)正确的 Enum 类中的方法:

class MyStrEnum(str, Enum):

    OK     = 'OK'
    FAILED = 'FAILED'

    def __str__(self) -> str:
        return self.value
Run Code Online (Sandbox Code Playgroud)

最好的


Noa*_*Nol 8

auto

定义枚举:

from enum import auto
from my_utils import AutoStrEnum

class MyEnum(AutoStrEnum):
    STATE_1 = auto()
    STATE_2 = auto()
Run Code Online (Sandbox Code Playgroud)

用它:

MyEnum.STATE_1 == "STATE_1"  # True
Run Code Online (Sandbox Code Playgroud)

my_utils.py

from enum import Enum

class AutoStrEnum(str, Enum):
    """
    StrEnum where enum.auto() returns the field name.
    See https://docs.python.org/3.9/library/enum.html#using-automatic-values
    """
    @staticmethod
    def _generate_next_value_(name: str, start: int, count: int, last_values: list) -> str:
        return name
Run Code Online (Sandbox Code Playgroud)


Glo*_*eye 5

str虽然和之间的 mixin 类Enum可以解决这个问题,但您还应该始终考虑为该工作找到合适的工具

有时,正确的工具很容易就是带有字符串值的 MODULE_CONSTANT 。例如,logging有一些常量,如 DEBUG、INFO 等,具有有意义的值 - 即使它们int在本例中是 s。

枚举是一个很好的工具,我经常使用它们。然而,它们主要是为了与同一枚举的其他成员进行比较,这就是为什么将它们与字符串等进行比较需要您跳过一个额外的环节。

  • 创建“Enum”是为了不需要不透明的模块常量。 (5认同)
  • 它们不会被弃用——它们将被相应的“IntEnum”取代。标准策略是保持 stdlib 尽可能稳定,这意味着不要大规模重写它以利用每个新功能。到目前为止,“http”、“socket”和“re”模块常量已被替换(也许还有其他几个我现在不记得了)。 (2认同)

Shi*_*hah 5

.name如果关联的字符串值是有效的 Python 名称,那么您可以使用如下属性获取枚举成员的名称:

from enum import Enum
class MyEnum(Enum):
    state1=0
    state2=1

print (MyEnum.state1.name)  # 'state1'

a = MyEnum.state1
print(a.name)  # 'state1'
Run Code Online (Sandbox Code Playgroud)

如果关联的字符串值是任意字符串,那么您可以这样做:

class ModelNames(str, Enum):
    gpt2 = 'gpt2'
    distilgpt2 = 'distilgpt2'
    gpt2_xl = 'gpt2-XL'
    gpt2_large = 'gpt2-large'

print(ModelNames.gpt2) # 'ModelNames.gpt2'
print(ModelNames.gpt2 is str) # False
print(ModelNames.gpt2_xl.name) # 'gpt2_xl'
print(ModelNames.gpt2_xl.value) # 'gpt2-XL'
Run Code Online (Sandbox Code Playgroud)

在线尝试:https: //repl.it/@sytelus/enumstrtest