如何在Python中表示枚举

时间:2018-09-25 08:55:03

标签: python enums

我知道,herethere中已经存在类似的问题。但是他们的问题和答案并非我要寻找的。此外,他们是锁定问题,因此我无法为他们添加新答案。 SMH。

首先,让我们澄清问题以了解其范围。在像这样的其他静态语言中使用枚举时:

public enum Size
{
    SMALL=0,
    MIDIUM=1,
    LARGE=2,
    BIG=2  // There can possibly be an alias
}

我们希望它能帮助我们

  1. 在引用值时要防止输入错误。例如,var foo = Size.SMALL有效,var bar = Size.SMAL应该会产生糟糕的错误。
  2. 枚举值可以支持字符串,例如HTTP404 = "Not Found", HTTP200 = "OK", ...。 (因此,基于range(N)的那些实现是不可接受的。)
  3. 在将参数定义为特定的Enum类型时,它充当仅接受此类值的规则。例如,public void Foo(Size size) {...}
  4. 我还希望这些值成为我的Enum解决方案中的一等公民。就是说,我的函数def parser(value_from_the_wire): ...想使用一些本机值(例如整数或字符串等),而不是使用Enum成员。这是Python 3中标准Enum中棘手的部分。

    • assert 2 == MY_ENUM.MY_VALUE仅在从IntEnum派生MY_ENUM时才有效(并且没有默认的StrEnum,尽管不难自己一个子类化)
    • assert 2 in MY_ENUM不起作用,即使MY_ENUM是从IntEnum派生的。

2 个答案:

答案 0 :(得分:1)

TL; DR :使用venum

因此,我满足该问题中3个条件的Python解决方案基于namedtuple,并且实现似乎比新的built-in Enum in Python 3更简单。

from collections import namedtuple

def enum(name=None, **kwargs):
    """
    :param name: An optional type name, which only shows up when debugging by print(...)
    """
    # This actual implementation below is just a one-liner, even within 80-char
    return namedtuple(name or "Const_%d" % id(kwargs), kwargs.keys())(**kwargs)

用法现在很简单。

# definition syntax
SIZE = enum("Size", SMALL=0, MEDIUM=1, LARGE=2, BIG=2)

# usage on referencing
print(SIZE.SMALL)   # got 0, instead of <SIZE.SMALL: 0>
try:
    print(SIZE.SMAL)    # got AttributeError
    assert False, "should not arrive this line"
except AttributeError:
    pass

# usage on comparison and contains-check
assert SIZE.MEDIUM == 1  # works. It won't work when using standard Enum (unless using IntEnum)
assert 1 in SIZE  # works. It won't work when using standard Enum (unless you wrote it as SIZE(1)).

# usage on regulating input value
def t_shirt_size(size):

    if size not in SIZE:
        raise ValueError("Invalid input value")

    print("Placing order with size: %s" % size)

t_shirt_size(SIZE.MEDIUM)   # works
t_shirt_size(2)             # also want this to work
try:
    t_shirt_size(7)             # got AssertionError
    assert False, "This line should not be reached!"
except ValueError:
    pass

编辑1:实际上,我知道有一个standard Enum module in Python 3,从功能上讲,它是我下面的单层实现的很大程度上超集。但是,在一种情况下,标准枚举不适合我的需求。我希望价值观成为我的枚举中的一等公民;我希望我的t_shirt_size(...)函数接受一个实际值,而不仅仅是一个枚举成员。标准枚举方法不允许这两种用法:assert SIZE.MEDIUM == 1assert 1 in SIZE

编辑2:鉴于人们倾向于将此主题定型为重复,我计划将我的方法实际实现为带有大量文档的独立模块。我什至为它起了一个很酷的名字,venum,V代表Value。就是在那个时候,我检查了pypi中的名称,发现已经有一个使用与我相同的方法和documented well使用相同名称的软件包。这样就解决了。我将简单地pip install venum代替。 :-)

答案 1 :(得分:0)

使用Python 3的Enum

from enum import IntEnum


class SIZE(Enum):

    SMALL = 0
    MEDIUM = 1
    LARGE = 2
    BIG = 2

    @classmethod
    def contains(cls, value):
        return any([e.value == value for e in cls])

并使用:

print(SIZE.SMALL)   # got <SIZE.SMALL: 0>
print(SIZE.SMAL)    # got AttributeError

def t_shirt_size(size):
    assert size in SIZE, "Invalid input value"
    place_order_with_size(size)

t_shirt_size(SIZE.MEDIUM)   # works
t_shirt_size(7)             # got AssertionError