自 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,但我不确定。
感谢您的建议!
如果您想将它们与
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
>>>
我之前没有遇到过 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
您也可以创建一个简单的装饰器来解决这个问题:
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 的答案中的其他方法
结合上述一些想法,您可以对 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
对于那些想要将
==
与两个枚举实例一起使用的人:enum_instance_1 == enum_instance_2
只需在 Enum 类中添加
__eq__
方法,如下所示:
def __eq__(self, other):
return self.__class__ is other.__class__ and other.value == self.value
我使用的是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)