如何在Python中比较枚举?

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

自 Python 3.4 起,存在

Enum
类。

我正在编写一个程序,其中一些常量具有特定的顺序,我想知道哪种方式是最Pythonic的方式来比较它们:

class Information(Enum):
    ValueOnly = 0
    FirstDerivative = 1
    SecondDerivative = 2

现在有一种方法,需要将

information
的给定
Information
与不同的枚举进行比较:

information = Information.FirstDerivative
print(value)
if information >= Information.FirstDerivative:
    print(jacobian)
if information >= Information.SecondDerivative:
    print(hessian)

直接比较不适用于枚举,因此有三种方法,我想知道首选哪一种:

方法一:使用价值:

if information.value >= Information.FirstDerivative.value:
     ...

方法2:使用IntEnum:

class Information(IntEnum):
    ...

方法 3:根本不使用枚举:

class Information:
    ValueOnly = 0
    FirstDerivative = 1
    SecondDerivative = 2

每种方法都有效,方法 1 有点冗长,而方法 2 使用不推荐的 IntEnum 类,而方法 3 似乎是在添加 Enum 之前执行此操作的方法。

我倾向于使用方法1,但我不确定。

感谢您的建议!

python python-3.x enums compare
6个回答
72
投票

如果您想将它们与

Enum
一起使用,您应该始终实现丰富的比较运算符。使用
functools.total_ordering
类装饰器,您只需要实现
__eq__
方法以及单个排序,例如
__lt__
。由于
enum.Enum
已经实现了
__eq__
这变得更加容易:

>>> import enum
>>> from functools import total_ordering
>>> @total_ordering
... class Grade(enum.Enum):
...   A = 5
...   B = 4
...   C = 3
...   D = 2
...   F = 1
...   def __lt__(self, other):
...     if self.__class__ is other.__class__:
...       return self.value < other.value
...     return NotImplemented
... 
>>> Grade.A >= Grade.B
True
>>> Grade.A >= 3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unorderable types: Grade() >= int()

可怕的、可怕的、可怕的事情可能会发生在

IntEnum
身上。它主要是为了向后兼容而包含在内,枚举过去是通过子类化
int
来实现的。来自文档

对于绝大多数代码,强烈建议使用 Enum,因为 IntEnum 打破了枚举的一些语义承诺(通过 与整数可比较,因此通过与其他不相关的传递性 枚举)。它应该只在特殊情况下使用 没有其他选择;例如,当整数常量替换为 需要枚举和向后兼容的代码 仍然期望整数。

以下是您不想这样做的示例:

>>> class GradeNum(enum.IntEnum):
...   A = 5
...   B = 4
...   C = 3
...   D = 2
...   F = 1
... 
>>> class Suit(enum.IntEnum):
...   spade = 4
...   heart = 3
...   diamond = 2
...   club = 1
... 
>>> GradeNum.A >= GradeNum.B
True
>>> GradeNum.A >= 3
True
>>> GradeNum.B == Suit.spade
True
>>> 

24
投票

我之前没有遇到过 Enum,所以我扫描了文档(https://docs.python.org/3/library/enum.html)...并找到了 OrderedEnum(第 8.13.13.2 节),这不是吗你想要什么?来自文档:

>>> class Grade(OrderedEnum):
...     A = 5
...     B = 4
...     C = 3
...     D = 2
...     F = 1
...
>>> Grade.C < Grade.A
True

2
投票

您也可以创建一个简单的装饰器来解决这个问题:

from enum import Enum
from functools import total_ordering

def enum_ordering(cls):
    def __lt__(self, other):
        if type(other) == type(self):
            return self.value < other.value

        raise ValueError("Cannot compare different Enums")

    setattr(cls, '__lt__', __lt__)
    return total_ordering(cls)


@enum_ordering
class Foos(Enum):
    a = 1
    b = 3
    c = 2

assert Names.a < Names.c
assert Names.c < Names.b
assert Names.a != Foos.a
assert Names.a < Foos.c # Will raise a ValueError

为了获得奖励积分,您可以实现上面 @VoteCoffee 的答案中的其他方法


1
投票

结合上述一些想法,您可以对 enum.Enum 进行子类化,使其与字符串/数字相当,然后在此类上构建枚举:

import numbers
import enum


class EnumComparable(enum.Enum):
    def __gt__(self, other):
        try:
            return self.value > other.value
        except:
            pass
        try:
            if isinstance(other, numbers.Real):
                return self.value > other
        except:
            pass
        return NotImplemented

    def __lt__(self, other):
        try:
            return self.value < other.value
        except:
            pass
        try:
            if isinstance(other, numbers.Real):
                return self.value < other
        except:
            pass
        return NotImplemented

    def __ge__(self, other):
        try:
            return self.value >= other.value
        except:
            pass
        try:
            if isinstance(other, numbers.Real):
                return self.value >= other
            if isinstance(other, str):
                return self.name == other
        except:
            pass
        return NotImplemented

    def __le__(self, other):
        try:
            return self.value <= other.value
        except:
            pass
        try:
            if isinstance(other, numbers.Real):
                return self.value <= other
            if isinstance(other, str):
                return self.name == other
        except:
            pass
        return NotImplemented

    def __eq__(self, other):
        if self.__class__ is other.__class__:
            return self == other
        try:
            return self.value == other.value
        except:
            pass
        try:
            if isinstance(other, numbers.Real):
                return self.value == other
            if isinstance(other, str):
                return self.name == other
        except:
            pass
        return NotImplemented

0
投票

对于那些想要将

==
与两个枚举实例一起使用的人:
enum_instance_1 == enum_instance_2

只需在 Enum 类中添加

__eq__
方法,如下所示:

def __eq__(self, other):
    return self.__class__ is other.__class__ and other.value == self.value

0
投票

我使用的是Python 3.9。 https://pypi.org/project/ordered-enum/

pip install ordered_enum

然后

from ordered_enum import OrderedEnum

class Information(OrderedEnum):
    ValueOnly = 0
    FirstDerivative = 1
    SecondDerivative = 2

print(Information.ValueOnly < Information.FirstDerivative)
© www.soinside.com 2019 - 2024. All rights reserved.