動かしながら学ぶPython入門~その３：条件演算と条件分岐~

# はじめに

この記事は、連載です。
前回の[動かしながら学ぶPython入門~その１：ソースファイル作成から画面に表示まで~](https://qiita.com/kira4845/items/a9459a56a8f78b97ee05)から[動かしながら学ぶPython入門~その２：変数操作と画面入力~](https://qiita.com/kira4845/items/203e47952a27f6ea6cee)を先に読んでからのほうが理解しやすいと思います。

タイトルに`【コラム】`とついているセクションは、動作や書式に直接の関係はありません。
ちゃっちゃと動かしてみたい人は読み飛ばしても問題ありません。
もちろん、せっかく書いたので読んでもらえたほうが嬉しいです。

## 対象
- `Python`の初歩中の初歩を学習したい人

## 執筆時の環境

- OS: Windows10
- Python: 3.7.3
- エディタ：VSCode

## 本文中の用語

| 用語 | 概略 |
|:--|:--|
|ターミナル|コマンドでコンピュータに命令を出すインターフェース<br>Windowsだと"コマンドプロンプト"や"PowerShell"が標準|
|Pythonインタプリタ|Pythonのソースコードの解釈と実行を行うアプリケーション<br>詳しくは[コラム][インタプリタとは]を参照|
|ソースファイル|アプリケーションの元となる、プログラム言語の決まった書式で書かれたファイル|

[数値型]:https://qiita.com/kira4845/items/20e54216f048961eddad#数値型
[int]:https://qiita.com/kira4845/items/20e54216f048961eddad#int
[float]:https://qiita.com/kira4845/items/20e54216f048961eddad#float
[complex]:https://qiita.com/kira4845/items/20e54216f048961eddad#complex
[文字列型]:https://qiita.com/kira4845/items/20e54216f048961eddad#文字列型
[str]:https://qiita.com/kira4845/items/20e54216f048961eddad#str
[シーケンス型]:https://qiita.com/kira4845/items/20e54216f048961eddad#シーケンス型
[list]:https://qiita.com/kira4845/items/20e54216f048961eddad#list
[tuple]:https://qiita.com/kira4845/items/20e54216f048961eddad#tuple
[range]:https://qiita.com/kira4845/items/20e54216f048961eddad#range
[set]:https://qiita.com/kira4845/items/20e54216f048961eddad#set
[dict]:https://qiita.com/kira4845/items/20e54216f048961eddad#dict
[bool]:https://qiita.com/kira4845/items/20e54216f048961eddad#bool

# 条件演算をやってみる(※例題なし)

## 条件演算とは

Pythonに限らず、プログラミングでは条件に応じて処理を変えることが頻出する。
この時、`等価`や`大小`など条件を評価するための演算を**条件演算**という。

条件演算は、演算結果として条件に合致するか否かを表す、`True`or`False`の2値のみを持つようになっている。
Pythonでは[bool]型があるので[bool]が返る。[bool]は[int]のサブクラスで、`0`を`False`、`0`以外を`True`(`1`)に丸めるようになっている。

## 条件演算子

実際にプログラミングするにあたって、条件の合否を組むのに使うのが**条件演算子**。
Pythonでは次のような条件演算子が存在する。

|演算子|意味|使用例|
|:--:|:--|:--|
| `==` | 左辺と右辺の値が等しい | `x == y` |
| `!=` | 左辺と右辺の値が異なる | `x != y` |
| `>` | 左辺の値が右辺の値を超過 | `x > y` |
| `>=` | 左辺の値が右辺の値以上 | `x >= y` |
| `<` | 左辺の値が右辺の値未満 | `x < y` |
| `<=` | 左辺の値が右辺の値以下 | `x <= y` |
| `is` | 左辺と右辺のオブジェクトが等しい | `x is y` |
| `is not` | 左辺と右辺のオブジェクトが異なる | `x is not y` |
| `in` | 左辺の値を右辺に含む | `x in y` |
| `not in` | 左辺の値を右辺に含まない | `x not in y` |

Pythonに限らず、主なプログラム言語において`=`は**右辺を左辺に代入**を表すので、等値の条件は`==`と`=`を2つ連続して書く。

また、以上・以下の`=`については必ず`>`/`<`の後に来ないといけない。

```python:対話モード
>>> 10 <= 0
False
>>> 10 =< 0
  File "<stdin>", line 1
    10 =< 0
        ^
SyntaxError: invalid syntax
```

恐らく、特にわかりにくいのが`is`/`is not`と`==`/`!=`の違いだと思う。
まず、`==`/`!=`については値が等しいかどうかを判定するので、**オブジェクトとしての等価判定はしない**。
例えば、次の例は`True`と評価される。

```python:対話モード
>>> [1, 2, 3] == [1, 2, 3]
True
```

ところが、`is`/`is not`では値だけではなく**オブジェクトとして等価かどうかも判定する**ので値が等しいだけのものは`False`と評価される。

```python:対話モード
>>> [1, 2, 3] is [1, 2, 3]
False
```

この使い分けは、慣れるまで結構混乱するのだが、要素一つ一つをオブジェクトとして見る感覚がついて来ると、だんだん使い分けられるようになる。

ちなみに、例のように全く内容が同じ[list]オブジェクトを、それぞれ変数に格納して、実際にオブジェクトのidを見てみると、別物であることがわかる。

```python:対話モード
>>> a = [1, 2, 3]
>>> b = [1, 2, 3]
>>> id(a)
1453917102792
>>> id(b)
1453917600392
```

## ブール演算子で条件式を組み合わせる

条件式を組み立てる時、2つ以上の条件式を**どちらも満たす**や**どれかを満たす**のような組み方をしたくなる時がある。
[前述](#条件演算とは)の通り、条件演算の結果は[bool]で返される為、**ブール演算子**を使うことで実現できる。

Pythonでは次のようにブール演算子を記述する。

|演算子|意味|使用例|
|:--:|:--|:--|
|`and`|左辺の評価が`False`の時は左辺の値を、`True`の時は右辺の値を返す|`x == y and x < z` |
|`or`|左辺の評価が`True`の時は左辺の値を、`False`の時は右辺の値を返す|`x == y or x < z` |
| `not` | 条件項の値が`True`なら`False`,`False`なら`True`にする | `not x == y` |

Pythonでは、条件式を`()`などで囲う既定はないが、複数の条件式をつなぐ時は条件ごとに`()`で囲われているほうが可視性はいいと思う。

```python:対話モードで実行
>>> 10 == 10.0 and 3 < 5
True
>>> (10 == 10.0) and (3 < 5)
True
```

また、ブール演算子は複数つなげて書くこともできる。

```python:対話モードで実行
>>> (10 == 10.0) and (3 < 5) and ('a' == 'b') 
False
```

この時、異なるブール演算子を組み合わせる場合、`and`>`or`の順で優先度があるため`()`の有無で結果が異なる場面があることに注意が必要。
例えば、次の例は`or`の演算を明示的に先行させるかどうかで結果が異なる。

```python:対話モードで実行
>>> (10 == 10.0) or (3 < 5) and ('a' == 'b')
True
>>> ((10 == 10.0) or (3 < 5)) and ('a' == 'b')
False
```

ちなみに、ブール演算子は各項を**ブール値として評価した場合**として処理する為、実は値自体がブール値である必要はない。
例えば、次の例は左辺の`0`をブール値として`False`として扱った結果、右辺の値を返している。

```python:対話モードで実行
>>> 0 or 12
12
```

ただし、`not`だけは**値をブール値として評価した結果の逆値を返す**ため、必ず[bool]値が返る。

```python:対話モードで実行
>>> not 1
False
>>> not 0
True
```

基本的には条件式の接続に使うと思うが、一応、[公式](https://docs.python.org/ja/3/reference/expressions.html#boolean-operations)でもこの特性を生かした例が載っているので、参考程度に見てみるといい。

# 条件分岐を使ってみる

Pythonにおける条件分岐は次の種類がある

[if]:#if
[elif]:#elif
[else]:#else

|識別子|意味|
|:--:|:--|
|[if]|条件式が`True`ならブロック下の処理を実行する|
|[elif]|[if]の条件式が`False`の時、条件式が`True`ならブロック下の処理を実行する|
|[else]|[if],[elif]共に条件式が`False`の時、ブロック下の処理を実行する|

## if

一番基本の条件分岐で、`if`に続いて条件式を記載し、終端の`:`後に改行・インデントしたうえで条件成立時の処理を記述する。

```python:書式
if 条件式:
    条件成立時の処理
```

[if]に限らないが、処理ブロック部分は対話モードだと`…`と表示されるので、続けてインデントして入力する。

```python:対話モードで実行
>>> if x > 10:
...     x = 10
...
>>> print(x)
10
```

対話モードでは、処理ブロックの終わりで改行しないとエラーになる。
ソースファイル実行では、改行してもしなくて問題なく認識される。

```python:対話モードで実行
>>> if x > 10:
...     x = 10
... print(x)
  File "<stdin>", line 3
    print(x)
        ^
SyntaxError: invalid syntax
```

なお、処理ブロック部のインデントがずれていると、その時点でエラーになる。

```python:対話モードで実行
>>> if x > 10:
... x = 10
  File "<stdin>", line 2
    x = 10
    ^
IndentationError: expected an indented block
```

### 例題

変数`x`,`y`2つの数値を標準入力から受けて、足した値を表示するプログラムを作った。

```python:if_only.py
x = float(input(">>>"))
y = float(input(">>>"))
print(x + y)
```

このプログラムでは`x`,`y`の入力値は無制限なので、**`10.0`を超える入力値の時は`10.0`に丸める処理を追加してみよう**。

また、丸めた際にはそのことを`xを10.0に制限しました`(`y`の場合は先頭文字は`y`)というメッセージでターミナルに出力すること。
なお、数値以外の入力があってもエラーが出るが、この例題においては対応は不要とする。

```text:実行例
> .\if_only.py
x = 1.0
y = 5.0
6.0
```
```text:実行例
> .\if_only.py
x = 12
y = 23
xを10.0に制限しました
yを10.0に制限しました
20.0
```

<details><summary><b>回答例を見る</b></summary><div>

```python
x = float(input("x = "))
y = float(input("y = "))

if x > 10.0:
    print("xを10.0に制限しました")
    x = 10.0

if y > 10.0:
    print("yを10.0に制限しました")
    y = 10.0

print(x + y)
```
</div></details>

## else

条件分岐において、条件式が成立ならAの処理、不成立ならBの処理というようなシチュエーションも頻発する。
そのような時は、[if]と抱き合わせる形で[else]を使う。

通常の[if]の構文後([if]の処理ブロック部の後)に、インデントを戻して`else:`と書いてから、改行・インデントして[if]の条件が不成立時の処理を書く。

```python:書式
if 条件式:
    条件成立時の処理
else:
    条件不成立時の処理
```

[else]は[if]と抱き合わせなので、当然単体では使えない。

```python:対話モードで実行
>>> else:
  File "<stdin>", line 1
    else:
       ^
SyntaxError: invalid syntax
```

また、対話モードでは[if]の処理ブロック部の後に改行すると、その[if]自体が閉じてしまうので、`else:`の記述は処理ブロックの後改行せずに、インデントだけを戻して記述する。

```python:対話モードで実行
>>> x = 5
>>> if x > 10:
...     x = 10
... 
>>> else:
  File "<stdin>", line 1
    else:
       ^
SyntaxError: invalid syntax
```
```python:対話モードで実行
>>> x = 5      
>>> if x > 10:
...     x = 10
... else:
...     x = 1
... 
>>> print(x)  
1
```

ソースファイル実行では、どちらでも認識されるが、改行しないほうが見やすいように思う。

### 例題

[ifの例題](#例題)と同じベースのプログラムを用意した。

```python:if_else.py
x = float(input(">>>"))
y = float(input(">>>"))
print(x + y)
```

`x`と`y`の合計値を表示後、**合計値が`20.0`を超える時は`x + yは20より大きいです`、`20.0`以下の時は`x + yは20以下です`とターミナルrに出力してみよう。

なお、数値以外の入力があってもエラーが出るが、この例題においては対応は不要とする。

```python:実行画面
> .\if_else.py
x = 12
y = 2
14.0
x + yは20以下です
```

```python:実行画面
> .\if_else.py
x = 12
y = 12
24.0
x + yは20より大きいです
```

<details><summary><b>回答例を見る</b></summary><div>

```python:if_else.py
x = float(input("x = "))
y = float(input("y = "))
result = x + y

print(result)

if  result > 20.0:
    print("x + yは20より大きいです")
else:
    print("x + yは20以下です")
```

この例では`20.0超過`の条件で判定しているが、もちろん`20.0以下`の条件判定で[if],[else]の処理内容が反転してる形でもよい。
</div></details>

## elif

[if]と[else]で条件の成立、不成立時それぞれに対応できるようになったが、実際のシチュエーションでは、3条件以上の分岐も普通に存在する。
[if]第一条件成立時に次の[if]で第二条件を見て・・・と組んでもいいのだが、条件が多いとどんどんネストが深くなってしまう。

そこで、[if]の条件が不成立時に[else]に行く前に別の条件を見ることができる[elif]が存在する。


通常の[if]の構文後([if]の処理ブロック部の後)に、インデントを戻して`elif 条件式:`と書いてから、改行・インデントして[if]の条件が不成立時且つ、[elif]の条件式が成立時の処理を書く。

```python:書式
if 第一条件式:
    第一条件成立時の処理
elif 第二条件式:
    第一条件不成立時且つ、第二条件成立時の処理
```

[elif]も[else]同様、[if]との抱き合わせなので、単体では使えない。

```python:対話モードで実行
>>> elif 5 > 10:
  File "<stdin>", line 1
    elif 5 > 10:
       ^
SyntaxError: invalid syntax
```

また、[elif]は同一の[if]]に対して複数定義することができ、すべての[if],[elif]の条件不成立時の[else]を併用することもできる。

```python:対話モードで実行
>>> x = 300
>>> if x < 5:
...     x = 5
... elif x < 7:
...     x = 7
... elif x > 100:
...     x = 100
... else:
...     x = 0
...
>>> print(x)
100
```

### 例題

[ifの例題](#例題)と同じベースのプログラムを用意した。

```python:if_else.py
x = float(input(">>>"))
y = float(input(">>>"))
print(x + y)
```

[ifの例題](#例題)同様、変数`x`,`y`のともに **`10.0`を超える入力値の時は`10.0`に丸め**、さらに **`5.0`未満でも`5.0`に丸める処理を追加してみよう**。

また、丸めた際にはそのことを`xを10.0に制限しました`(`y`の場合は先頭文字は`y`, 丸め先が`5.0`の場合は`10.0`のところが`5.0`)というメッセージでターミナルに出力すること。
なお、数値以外の入力があってもエラーが出るが、この例題においては対応は不要とする。

```python:実行画面
> .\if_elif.py
x = 5
y = 8
13.0
```

```python:実行画面
> .\if_elif.py
x = 12.0
y = 2.0
xを10.0に制限しました
yを5.0に制限しました
15.0
```

<details><summary><b>回答例を見る</b></summary><div>

```python:if_else.py
x = float(input("x = "))
y = float(input("y = "))

if x > 10.0:
    print("xを10.0に制限しました")
    x = 10.0
elif x < 5.0:
    print("xを5.0に制限しました")
    x = 5.0
else:
    pass

if y > 10.0:
    print("yを10.0に制限しました")
    y = 10.0
elif y < 5.0:
    print("yを5.0に制限しました")
    y = 5.0
else:
    pass

print(x + y)
```

この例では[else]節を記述して`pass`としているが、これは[else]節が何も処理しないことを明示するもので、動作上はあっても無くても同じ動きをする。

ただ、明示することで、第3者や実装者自身が時間を空けて見返した時などに、**すべての条件が不成立の時の処理を書き漏れ・検討漏れではないことを示す**という意味があるので、書くようにしている。
</div></details>

## 【おまけ】3項演算子

条件で処理を切り分けるのは[if]~[elif],[else]だが、条件に応じて値を決定するだけならば、**3項演算子**も使える。

Pythonではこのように記述する。

```python:書式
条件成立時の値 if 条件文 else 条件不成立時の値
```

例えば、絶対値を計算するために**負数の時だけ-1を掛ける**という処理をしたければ、3項演算子を使って係数を`1`と`-1`から決定してあげるとスマートに書ける。(abs関数あるけど…)

```python:ternaly_operator.py
x = float(input("x = "))

x *= 1 if x >= 0 else -1
print(x)
```

```text:実行画面
> .\ternaly_operator.py
x = 10
10.0
```

```text:実行画面
> .\ternaly_operator.py
x = -10
10.0
```

[if],[else]でも対応可能だが、値選択ごときで何行も書きたくはないので、覚えておくとスマートに書けて便利。
未検証だが、[if],[else]に対して1行記述なので、インタプリタの解読が多分早いと思う(違ったらごめんなさい)。

## 【コラム】Pythonにおける処理ブロックの区切り方

条件分岐を使うにあたって、条件に該当した部分とそれ以外を区分ける必要がある。
条件分岐だけに限らず、繰り返し処理(ループ)の範囲や、共通処理(関数)の範囲として複数の処理の塊を区分けるのは、プログラミングでは頻出する。

Pythonでは、そういったブロックの範囲を**インデント**で表すことになっている。
他の言語では、`()`や`{}`などで括った範囲で表すことも多いが、括った範を示す**可視性の観点でインデントも合わせて使う**ことが慣例的に多い。
ただ、初学者などの理由から、こういった慣例を知らなかったりしてインデントが崩れた構文で書いてしまっても`()`があっていれば動くので、指摘されたりするまで気づかないこともある。

この点、Pythonでは言語の仕様としてインデントが定められているので、**動かすためには誰であっても同一のインデントにそろえる**必要があり、動いた時点でブロック回りの可視性がそれなりに担保できるという利点がある。

# 次回

動かしながら学ぶPython入門~その４：繰り返し~

# 参考

- [演算子の優先順位](https://www.javadrive.jp/python/num/index3.html)