パラメータの値を元に、PythonのEnumで定義した値を取得するメソッドを作ろうとしました。しかし、PythonのEnumの知識が弱かったため、事前に定義していたEnumをすべて取得できない事象が発生しました。
この記事では、PythonのEnumの仕様を確認して、Enumで定義した値をすべて取得できなかった原因をメモします。
環境
- Python
- 3.8.6
for文でEnumの値が取得できなかった原因
Enumに定義していた値(_value_
)が重複していたため。
まとめ
- 名前(name)の重複は許さない
- 値(value)の重複は許す。しかし、実行時に名前ごと欠落する
- Enumをタプルで定義している場合に、
_value_
を上書きすると怪しい挙動になる - 値が重複した状態でも、全部の名前を出力する方法はある。しかし、期待した値を取得できないことがある
動作確認
名前の重複は許さない
名前が重複している場合、実行時にエラーが発生します。
from enum import Enum class Color1(Enum): RED = 1 RED = 2 # TypeError: Attempted to reuse key: 'RED
値の重複は許すが、実行時に欠落する
名前と違って値は重複しても、実行時エラーは発生しません。しかし、値が重複していると名前ごと欠落します。
from enum import Enum class Color1(Enum): RED = 1 BLACK = 2 WHITE = 2
def test_foo(): for element in Color1: print(element) # 出力される値 # Color1.RED # Color1.BLACK # 出力されない # Color1.WHITE
Enumをタプルで持たせたケース
ちなみに、私がハマったのは亜種です。Enumをタプルで持たせていたのですが、値を上書きしていたために目的の名前を取得できませんでした。_value_
を上書きしない場合は、タプルのまま_value
にセットされますので問題は発生しません。
class Color2(Enum): RED = (1, 1) BLACK = (2, 2) WHITE = (3, 2) def __init__(self, id, value): self.id = id # この1行で_value_を上書きすると、値重複が発生する self._value_ = value
値が重複していても、名前を取得する
値が重複していた状態でも、名前を取得する方法はあります。定義したEnumに対して__members__.items():
を使用することで、すべての名前を取得できます。ただし、注意してください。名前は取得できますが、値は取得できない可能性があります。
先ほどのケースであるタプルでEnumを指定しており、_value_
を上書きしているケースの場合、期待しない挙動をします。次の例では、WHITE = (3, 2)
と定義しているので、取得したい値は(3, 2)
ですが、_value_
を上書きしているためにBLACK
である(2, 2)
を取得してしまいます。
class Color2(Enum): RED = (1, 1) BLACK = (2, 2) WHITE = (3, 2) def __init__(self, id, value): self.id = id self._value_ = value
def test_bar(): for name, element in Color2.__members__.items(): print(name) print(element) # 出力される値 # RED # Color2.RED # BLACK # Color2.BLACK # WHITE # Color2.BLACK # WHITEの下のColor2.BLACKはtypoではありません。
値の重複を許容しないようにする
Enum
標準ライブラリのunique
アノテーションを使用すると、値が重複していた際にも実行時エラーを発生させられます。
from enum import Enum, unique @unique class Color3(Enum): RED = (1, 1) BLACK = (2, 2) WHITE = (3, 2) def __init__(self, id, value): self.id = id self._value_ = value # E ValueError: duplicate values found in <enum 'Color3'>: WHITE -> BLACK
ソースコード
- python-practice/Color.py at 8855131da409c67be5040586015f8d58d4953a64 · hirotoKirimaru/python-practice · GitHub
- python-practice/test_enum.py at 8855131da409c67be5040586015f8d58d4953a64 · hirotoKirimaru/python-practice · GitHub
終わりに
Pythonはメインで使用していない言語ですので、たまにハマりますね。
私には@unique
で値を重複を許容しないようにするのか、__members__.items():
を使用して値の重複を許容するのか、どちらがよい設計かは判断できません。@unique
を使いつつ、タプルで定義して一意なIDを持たせておく方法が良さそうな予感はしていますが。
どちらにしてもPythonらしくない面倒な書き方だと思うので、もっと直感的に書けるようにしてほしいです。
この記事がお役に立ちましたら、各種SNSでのシェアや、今後も情報発信しますのでフォローよろしくお願いします。