web-dev-qa-db-ja.com

Pythonで列挙型を比較す​​る方法は?

Python 3.4なので、Enumクラスが存在します。

私はいくつかの定数に特定の順序があるプログラムを書いていますが、それらを比較するのに最もPythonyなのはどの方法なのでしょうか。

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

informationInformationを異なる列挙型と比較する必要があるメソッドがあります。

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

直接比較はEnumでは機能しないため、3つのアプローチがあり、どちらが好ましいか疑問に思います。

アプローチ1:値を使用する:

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

アプローチ2:IntEnumを使用する:

class Information(IntEnum):
    ...

アプローチ3:Enumをまったく使用しない:

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

各アプローチが機能します。アプローチ1はもう少し冗長ですが、アプローチ2は推奨されないIntEnumクラスを使用し、アプローチ3はEnumが追加される前にこれを行ったようです。

私はアプローチ1を使用する傾向がありますが、よくわかりません。

アドバイスをありがとう!

18
Sebastian Werk

以前に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
11
nigel222

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をサブクラス化することで実装されていました。 docs から:

大部分のコードでは、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
>>> 
29