如何在不使用 try/catch 的情况下测试 Python Enum 是否存在 int 值?

使用 Python Enum 类,有没有一种方法可以不使用 try/catch 来测试 Enum 是否包含特定的 int 值?

下列课程:

from enum import Enum


class Fruit(Enum):
Apple = 4
Orange = 5
Pear = 6

如何测试值6(返回 true)或值7(返回 false) ?

111691 次浏览

你可以使用 Enum.__members__-将名称映射到成员 的有序字典:

In [12]: 'Apple' in Fruit.__members__
Out[12]: True


In [13]: 'Grape' in Fruit.__members__
Out[13]: False

测试数值

变体1

请注意,Enum有一个名为 _value2member_map_的成员(没有文档记录,在以后的 python 版本中可能会更改/删除) :

print(Fruit._value2member_map_)
# {4: <Fruit.Apple: 4>, 5: <Fruit.Orange: 5>, 6: <Fruit.Pear: 6>}

您可以根据这个映射测试一个值是否在您的 Enum中:

5 in Fruit._value2member_map_  # True
7 in Fruit._value2member_map_  # False

变体2

如果你不想依赖这个特性,这是一个替代方案:

values = [item.value for item in Fruit]  # [4, 5, 6]

或者(可能更好) : 使用 set; in操作符将更有效率:

values = set(item.value for item in Fruit)  # {4, 5, 6}

然后测试

5 in values  # True
7 in values  # False

has_value添加到类中

然后你可以把它作为一个方法添加到你的类中:

class Fruit(Enum):
Apple = 4
Orange = 5
Pear = 6


@classmethod
def has_value(cls, value):
return value in cls._value2member_map_


print(Fruit.has_value(5))  # True
print(Fruit.has_value(7))  # False

检查钥匙

如果您想测试名称(而不是值) ,我会使用 _member_names_:

'Apple' in Fruit._member_names_  # True
'Mango' in Fruit._member_names_  # False

看看是不是在 Enum. _value2member_map_

In[15]: Fruit._value2member_map_
Out[15]: {4: <Fruit.Apple: 4>, 5: <Fruit.Orange: 5>, 6: <Fruit.Pear: 6>}


In[16]: 6 in Fruit._value2member_map_
Out[16]: True


In[17]: 7 in Fruit._value2member_map_
Out[17]: False

建立在里达 · 马奇开创的基础上:

6 in Fruit.__members__.values()

返回 True

7 in Fruit.__members__.values()

返回 False

不要。

如果使用 Enum,则可以使用

     if isinstance(key, Fruit):

但是,除此之外,try. . 是测试 enum 的 Python 方法。

对 IntEnum 中的 int 进行测试的正确的 Python 方法是尝试一下,并在出现故障时捕获 ValueError。

上面提出的许多解决方案都被主动否定,并且将在3.8中被禁用(“ DerecationPolice: 在遏制检查中使用非 Enums 将引发 Python 3.8中的 TypeError”)

如果您真的对保持代码的现代化不感兴趣,那么您可以直接使用

if key in Fruit:

EAFP版的答案是:

try:
Fruit(val)
return True
except ValueError:
return False

您可以使用 __members__特殊属性对成员进行迭代:

from enum import Enum


class Fruit(Enum):
Apple = 4
Orange = 5
Pear = 6


@staticmethod
def has_value(item):
return item in [v.value for v in Fruit.__members__.values()]

还有一个没有人提到的解决方案:

is_value_in_fruit = any(f.value == value_to_check for f in Fruit)

此外,如果你使用 IntEnum而不是 Enum,(class Fruit(IntEnum))你可以这样做

is_value_in_fruit = any(f == value_to_check for f in Fruit)

IntEnum + __members__

你可以使用 IntEnum__members__来达到所需的行为:

from enum import IntEnum


class Fruit(IntEnum):
Apple = 4
Orange = 5
Pear = 6


>>> 6 in Fruit.__members__.values()
True
>>> 7 in Fruit.__members__.values()
False

Enum + 列表内涵 + .value

如果你必须/想要坚持 Enum,你可以这样做:

>>> 6 in [f.value for f in Fruit]
True
>>> 7 in [f.value for f in Fruit]
False

EAPF + ValueError

或者你可以使用 请求原谅比请求许可更容易方法:

try:
Fruit(x)
except ValueError:
return False
else:
return True

有一种方法可以让所有枚举都能够检查一个项目是否存在:

import enum


class MyEnumMeta(enum.EnumMeta):
def __contains__(cls, item):
return item in [v.value for v in cls.__members__.values()]


class MyEnum(enum.Enum, metaclass=MyEnumMeta):
FOO = "foo"
BAR = "bar"

现在你可以做一个简单的检查:

>>> "foo" in MyEnum
True

如果所有枚举的值总是相同的类型,那么它甚至可以变得更简单——例如字符串:

import enum
 

class MyEnumMeta(enum.EnumMeta):
def __contains__(cls, item):
return item in cls.__members__.values()


class MyEnum(str, enum.Enum, metaclass=MyEnumMeta):
FOO = "foo"
BAR = "bar"

编辑: 另一个版本,技术上最正确的版本:

import enum


class MyEnumMeta(enum.EnumMeta):
def __contains__(cls, item):
try:
cls(item)
except ValueError:
return False
else:
return True


class MyEnum(enum.Enum, metaclass=MyEnumMeta):
FOO = "foo"
BAR = "bar"

如果枚举有许多成员,这种方法可以更快,因为它不生成新列表,并且在找到给定值时停止对枚举的遍历:

any(x.value == 5 for x in Fruit)  # True
any(x.value == 7 for x in Fruit)  # False

这个怎么样?

from enum import Enum


class Fruit(Enum):
Apple = 4
Orange = 5
Pear = 6


has_apples = 4 in [n.value for n in Fruit]

这也会让你做到:

has_apples = "Apple" in [n.name for n in Fruit]

我只是将一个 IntEnum 转换成一个列表,然后正常测试它:

from enum import IntEnum
class Foo(IntEnum):
ONE = 1
TWO = 2
THREE = 3


print(1 in list(Foo))
True
print(4 in list(Foo))
False
class MyEnumMixin:


raw_values = None  # for IDE autocomplete


def __new__(cls, value):
if 'raw_values' not in cls.__dict__:
cls.raw_values = set()
cls.raw_values.add(value)
if cls.__bases__[0] is MyEnumMixin:
member = object().__new__(cls)
else:
member = super().__new__(cls, value)
member._value_ = value
return member






class MyEnum(MyEnumMixin, Enum):
FOO = 1
BAR = 'bar'


print(1 in MyEnum.raw_values)
print('bar' in MyEnum.raw_values)






class MyStrEnumMixin(MyEnumMixin, str):
pass


class MyStrEnum(MyStrEnumMixin, Enum):
FOO = 'foo'
BAR = 'bar'


print('foo' in MyStrEnum.raw_values)
print('bar' in MyStrEnum.raw_values)