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

问题描述 投票:0回答:16

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

有以下课程:

from enum import Enum

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

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

python enums
16个回答
239
投票

测试值

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

从 python 3.9 开始(?)python 提供了

IntEnum
。有了这些你就可以做到这一点:

from enum import IntEnum

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

print(6 in iter(Fruit))  # True

注意,无需创建

list
;只需迭代
iter(Fruit)
即可。再次强调,如果重复需要,可能值得像上面那样创建一个集合:

values = set(Fruit)
print(5 in values)  # True

测试按键

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

_member_names_

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

46
投票

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

import enum 

class MyEnumMeta(enum.EnumMeta): 
    def __contains__(cls, item): 
        return isinstance(item, cls) or 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"

编辑:根据@MestreLion的评论,我更新了第一个示例以包含

isinstance
检查;如果没有它,如果我们检查已经实例化的枚举值,测试就会失败。

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

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"

24
投票

您可以使用

Enum.__members__
- 将名称映射到成员的有序字典:

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

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

19
投票

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

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

15
投票

我只是将 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

12
投票

以 Reda Maachi 的起步为基础:

6 in Fruit.__members__.values() 

返回真

7 in Fruit.__members__.values()  

返回错误


7
投票

答案的EAFP版本:

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

7
投票

本来就不要。

如果您使用 Enum,您可以使用

测试枚举
     if isinstance(key, Fruit):

但除此之外, try.. 是测试枚举的 Python 方式。事实上,对于鸭子打字范式的任何突破。

测试 IntEnum 中 int 的正确且 Python 的方法是尝试一下,如果失败则捕获 ValueError。

上面提出的许多解决方案已被积极弃用,并将在 3.8 版本中被禁止(“DeprecationWarning:在包含检查中使用非枚举将在 Python 3.8 中引发 TypeError”)

从 Python 3.8 开始 – 将其强制转换为列表。

在 3.8 左右之后,您可以通过将枚举强制转换为列表来进行测试,如下所示(使用 IntEnum)

from enum import IntEnum
class Fruit(IntEnum):
     Kiwi = 2
     Apple = 4
     Orange = 5
     Pear = 6

for x in range(8):
     print(f'{x}, {x in list(Fruit)}')

将打印如下

0, False
1, False
2, True
3, False
4, True
5, True
6, True
7, False

6
投票

您可以使用

__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()]

4
投票

只需检查它是否在

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

3
投票

还有另一种还没有人提到的衬垫解决方案:

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
投票

这个怎么样?

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]

1
投票

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
投票

我发现最干净、最熟悉的方法是使用

try/except
:

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

# If you want it in function form
def is_fruit(x):
    try:
        Fruit(x)
    except ValueError:
        return False
    else:
        return True

0
投票

总结以上所有内容并创建一个可以用来代替简单 Enum 的新类:

from enum import Enum, EnumMeta
    
    
class BetterEnumMeta(EnumMeta):
    def __contains__(cls, item):
        if isinstance(item, Enum):
            return item.__class__ is cls
    
        return any(item == member.value for member in cls.__members__.values())
    
    
class BetterEnum(Enum, metaclass=BetterEnumMeta):
    ...

将代码放在

better_enum.py
中,只需导入
BetterEnum
而不是
enum.Enum
并像往常一样使用扩展的“in”检查:

from better_enum import BetterEnum

class Fruit(int, BetterEnum):
    Apple = 4
    Orange = 5
    Pear = 6

class Colors(str, BetterEnum):
    red = "red"
    blue = "blue"
    black = "black"

通过这个子类化,您将有简单的方法来检查您的枚举是否具有任意值:

6 in Fruit # returns True
"Banana" in Fruit # returns False
"blue" in Colors # returns True
"Banana" in Colors # returns False

-1
投票
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)

© www.soinside.com 2019 - 2024. All rights reserved.