普段、pythonでコードを書いているのですが、先日@dataclass
とEnumの組み合わせではまったので備忘録として記載します。
pythonについては既存のコードを見つついじる程度の知識しかないので、そもそもこんな風につかわないよ!等あるかもしれません。
既存コードを参考に書いていましたが、Enumに@dataclass
つける意味あるのか?と最近ではうっすら思っていますが、事象として興味深かったのでまとめました。
どうはまったのか
以下のようなEnumを定義しました。
@dataclass
class Fruits(Enum):
APPLE = 0
BANANA = 1
CHERRY = 2
処理の都合上、APPLEとBANANAのみ処理をしたかったので、以下のようなコードを描きました。
if fruit in [Fruits.APPLE, Fruits.BANANA]:
# リンゴかバナナであれば処理をする
fruit=Fruits.CHERRY
を代入し、実行したところ、処理が行われてしまいました。
この辺りを整理してみたところ、以下のような結果となりました。
式 | 結果 | |
---|---|---|
Fruits.CHERRY in Fruits | True | |
Fruits.APPLE in [Fruits.APPLE, Fruits.BANANA] | True | |
Fruits.CHERRY in [Fruits.APPLE, Fruits.BANANA] | True | Falseを想定したのにTrueになっていた |
Fruits.CHERRY is Fruits.APPLE or Fruits.CHERRY is Fruits.BANANA | False |
dataclassを付けない場合
一方で、以前に同様のコードを書いたときは動いていた記憶があったので、確認してみるとそのコードには@dataclass
がついていませんでした。
class Vegetables(Enum):
AVOCADO = 0
BROCCOLI = 1
CARROT = 2
こちらは以下のような結果となりました。
式 | 結果 | |
---|---|---|
Vegetables.AVOCADO in Vegetables | True | |
Vegetables.AVOCADO in [Vegetables.AVOCADO, Vegetables.BROCCOLI] | True | |
Vegetables.CARROT in [Vegetables.AVOCADO, Vegetables.BROCCOLI] | False | 想定通り |
Vegetables.CARROT is Vegetables.AVOCADO or Vegetables.CARROT is Vegetables.BROCCOLI | False |
なぜ?
問題の起こった評価式をログで出力してみたところ、以下のようになりました。
式 | 結果 | |
---|---|---|
[Fruits.APPLE, Fruits.BANANA] | [Fruits(), Fruits()] |
|
[Vegetables.AVOCADO, Vegetables.BROCCOLI] | [<Vegetables.AVOCADO: 0>, <Vegetables.BROCCOLI: 1>] |
@dataclass
がついていない場合(Vegetables)はそれぞれの要素になっているのに対して、@dataclass
がついている場合(Fruits)はオブジェクトになっている?ようです。(ちょっとこへんよくわかっていないです)
これによってfruit in [Fruits.APPLE, Fruits.BANANA]
はfruitにCHERRYが入っていても、Fruitsの要素だったらオッケー!といった評価になったようです。
最後に
この事象はユニットテストが通らなかったことで発見したので、ユニットテストは本当に大事だなと思います。
既存コードにあるからといって、何となくコピーで使うのは良くないなと改めて思いました。
やっぱり、Enumに@dataclass
ついてるのおかしくない?