比較演算でつまづいた話

ちょっとイキって比較演算子を使っていい感じに書いてコード量を減らそうとしたら、想定外のところでつまづいたので備忘録的に残しておこうと思います．

# 環境
- macOS Catalina 10.15.1
- Python 3.7.5


# 対象読者
自分

# 演算子一覧
pythonには以下のような比較演算子があり、分岐などの条件文に使用したりします。

|演算子|結果|
|:--:|:----|
|x < y|x が y より小さければ True|
|x <= y|x が y より小さいか等しければ True|
|x > y|x が y より大きければ True|
|x >= y|x が y より大きいか等しければ True|
|x == y|x と y の値が等しければ True|
|x != y|x と y の値が等しくなければ True|
|x is y|x と y が同じオブジェクトであれば True|
|x is not y|x と y が同じオブジェクトでなければ True|
|x in y|x が y に含まれていれば True|
|x not in y|x が y に含まれていなければ True|

他にも以下のような論理演算子が存在し、これらを組み合わせることで自由度の高い条件文を作成することが可能です。


|演算子|結果|
|:--:|:---|
|x and y|x が True で y も True のとき y を返す。x が False のとき x を返す|
|x or y|x が True のとき x を返す。x が False のとき y を返す|
|not x|x が True であれば False、x が False であれば True を返す|

# 論理演算子の結果が妥当か確認する方法
`and`や`or`の場合は、左辺がTrueになるかFalseになるかが重要になってきます。これについては`bool`を使えば簡単に確認できます。

## bool
TrueやFalseは当然そのままの結果が返ってきます。

```terminal
>>> bool(True)
True
>>> bool(False)
False
```

## 数値
数値型の場合は0以外はTrueが返ってきます。

```terminal
>>> bool(1)
True
>>> bool(0)
False
>>> bool(-1)
True
>>> bool(0.5)
True
>>> bool(1j)
True
>>> bool(0j)
False
>>> bool(-1j)
True
```

## 文字列
文字列型では文字が存在している場合にTrueになります。

```terminal
>>> bool('')
False
>>> bool('hoge')
True
>>> bool(' ')
True
```

これはstrの**インスタンスが生成されているかを見ているわけではない**ので、注意が必要です。

```terminal
>>> type('')
<class 'str'>
```

## None
NoneもFalseとして扱われます。

```terminal
>>> bool(None)
False
```

## 配列
配列は要素が1つ以上存在する時にTrueが返ってきます。

```terminal
>>> bool([])
False
>>> bool([""])
True
>>> bool([None])
True
```

# つまづいた点
これまで見てきた通り非常に容易だとは思うのですが、注意するべき点は**要素が存在している時にTrueが返ってくるとは限らない**ということです。

どういう意味かを説明する前に、一旦Falseになる条件を以下にまとめてみました。

```terminal
>>> bool(False)
False
>>> bool(0)
False
>>> bool('')
False
>>> bool(None)
False
>>> bool([])
False
```

これを見る限り中身がなければFalseになりそうなところです。

そこで以下のようなコードを実行してみました。

```terminal
>>> import numpy as np
>>> hoge = None
>>> fuga = True
>>> if fuga:
...     hoge = np.arange(10)
>>> bazz = hoge if hoge else list(range(10))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
```

**まさかのエラー！**
個人的にやりたかったこととしては、条件式(fuga)がTrueのときndarrayを生成する関数が存在しており、別の関数ではhogeが生成されているかを判断し、新たに生成し直すかそのまま使用したいと考えていました。

解決策としてはインスタンスを生成しているかの確認ではなく、Noneであるかを判断基準に用いました。

```terminal
>>> import numpy as np
>>> hoge = None
>>> fuga = True
>>> if fuga:
...     hoge = np.arange(10)
>>> bazz = hoge if hoge is not None else list(range(10))
>>> bazz
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
```

ただし、この場合だとhogeにndarray以外が生成されていてもhogeを返すので、ndarray限定にしたい場合は要検討という感じですね...

## 追記 (2020/3/10)
hogeの中身がndarrayか確認する方法としては、`isinstance`という組み込み関数があるので、それを用いることで確認することが可能です。

```terminal
>>> hoge = np.arange(10)
>>> hoge
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>>
>>> isinstance(hoge, np.ndarray)
True
>>>
>>> hoge = None
>>> fuga = True
>>> if fuga:
...     hoge = np.arange(10)
...
>>>
>>> bazz = hoge if isinstance(hoge, np.ndarray) else list(range(10))
>>> bazz
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>>
>>> bazz = hoge if isinstance(hoge, list) else list(range(10))
>>> bazz
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
```

# 雑談
外部ライブラリをboolで判定するとFalseが返ってくることには驚きでした。まぁ、イキって変な書き方しようとした自分が悪いわけですが...
使いこなすにはまだまだ知識が足りてないことを痛感してしまいますが、めげずに日々精進していきたいものです(・∀・)


# 参考
[Pythonのif文による条件分岐の書き方](https://note.nkmk.me/python-if-elif-else/)