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


83

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

使用以下课程:

from enum import Enum

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

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

Answers:


124

测试价值

变体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

5
我个人更喜欢第二种解决方案。谢谢。
Aliakbar Abbasi'9

2
两种建议都不是特别节省内存或时间的,尽管在这些方面也不是很沉重。就我而言,我正在使用HTTPStatus标准库中的枚举,该枚举目前包含57个条目。我永远不会处理枚举中的全部值列表,因此我想避免缓存它们。
Acsor

1
@none,如果您担心速度,可以set为查找创建一个。这将使用一些额外的内存,但不会占用大量内存...
hiro主角

为什么在返回值附近加上括号?
康斯坦丁

必须return any(value == item.value[0] for item in cls) 与Python3一起使用时Enum结构是否发生了变化?
lony


7

在Reda Maachi开始的基础上:

6 in Fruit.__members__.values() 

返回True

7 in Fruit.__members__.values()  

返回False


2
这看起来很干净。不幸的是,当我对此进行测试(至少在Py3.7中)时,它不是那样工作的。Fruit .__ members __。values()的计算结果为odict_values([<Fruit.Apple:4>,<Fruit.Orange:5>,<Fruit.Pear:6>]),并且上面6和7的测试均返回False我。但这返回了真实:>>> Fruit.Orange in Fruit.__members__.values() True
nrshapiro

5
我想指出的是,这仅适用于IntEnum。
Yandros

5

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

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"

3

只是检查它是否在 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

4
_value2member_map_是未记录的功能,因此最好避免使用它。
Palasaty


2

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

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) 

2

如果枚举有很多成员,则此方法会更快,因为它不会创建新列表,并且在找到给定值时会停止遍历枚举:

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

1
这应该更高...简单有效,谢谢!
Magoo


0

别。

如果您使用的是Enum,则可以使用

     if isinstance(key, Fruit):

但是,否则,尝试..是测试枚举的Python方法。确实,对于鸭式范例的任何突破。

在IntEnum中测试int的正确方法是pythonic,是允许它执行并在失败时捕获ValueError。

上面建议的许多解决方案均已被3.8弃用(“ DeprecationWarning:在包含检查中使用非枚举会在Python 3.8中引发TypeError”)

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

if key in Fruit:

1
这种解决方案实际上是错误的。由于OP询问如何检查某个值是否在枚举中(例如6或7),所以这将始终返回false,因为枚举的任何成员实际上都不等于6或7。请尝试一下。
约书亚·瑞安

0

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

0

这个怎么样?

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]

0

我只是将一个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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.