如何测试具有特定名称的 Enum 成员是否存在?

How to test if an Enum member with a certain name exists?

使用Python 3.4 我想测试一个枚举class是否包含一个具有特定名称的成员。

示例:

class Constants(Enum):
    One = 1
    Two = 2
    Three = 3

print(Constants['One'])
print(Constants['Four'])

给出:

Constants.One
  File "C:\Python34\lib\enum.py", line 258, in __getitem__
    return cls._member_map_[name]
KeyError: 'Four'

我可以捕获 KeyError 并将异常作为存在的指示,但也许有更优雅的方法?

您可以使用 Enum.__members__ - an ordered dictionary mapping names to members:

In [12]: 'One' in Constants.__members__
Out[12]: True

In [13]: 'Four' in Constants.__members__
Out[13]: False

我想说这属于 EAFP(请求宽恕比请求许可更容易),这是 Python.

相对独特的概念

Easier to ask for forgiveness than permission. This common Python coding style assumes the existence of valid keys or attributes and catches exceptions if the assumption proves false. This clean and fast style is characterized by the presence of many try and except statements. The technique contrasts with the LBYL style common to many other languages such as C.

这与 LBYL(三思而后行)形成对比,当您说您正在寻找“一种更优雅的方式”时,我认为您想要的是这种方式。

Look before you leap. This coding style explicitly tests for pre-conditions before making calls or lookups. This style contrasts with the EAFP approach and is characterized by the presence of many if statements.

In a multi-threaded environment, the LBYL approach can risk introducing a race condition between “the looking” and “the leaping”. For example, the code, if key in mapping: return mapping[key] can fail if another thread removes key from mapping after the test, but before the lookup. This issue can be solved with locks or by using the EAFP approach.

因此根据文档,实际上最好使用 try/except 块来解决您的问题。

TL;DR

使用 try/except 块来捕获 KeyError 异常。

可以使用以下方法来测试名称是否存在:

if any(x for x in Constants if x.name == "One"):
  # Exists
else:
  # Doesn't Exist

使用 x.value 来测试枚举值:

if any(x for x in Constants if x.value == 1):
  # Exists
else:
  # Doesn't Exist

为了提高易读性,您可以把这些建议放在上面作为class方法。 例如:

class Constants(Enum):
    One = 1
    Two = 2
    Three = 3

    @classmethod
    def has_key(cls, name):
        return name in cls.__members__ # solution above 1
        # return any(x for x in cls if x.name == name) # or solution above 2

为了使用:

In [6]: Constants.has_key('One')
Out[6]: True

In [7]: Constants.has_key('Four')
Out[7]: False

阅读 the source code Enum class:

def __call__(cls, value, names=None, *, module=None, qualname=None, type=None, start=1):
"""Either returns an existing member, or creates a new enum class.

因此,根据文档字符串注释,检查成员资格的 Pythonic 方法是:

from enum import Enum

class TestEnum(Enum):
    TEST = 'test'

def enum_contains(enum_type, value):
    try:
        enum_type(value)
    except ValueError:
        return False
    return True

>>> enum_contains(TestEnum, 'value_doesnt_exist')
False
>>> enum_contains(TestEnum, 'test')
True