第5回 条件分岐#


Open In Colab


この授業で学ぶこと#

この授業では、条件に応じて処理内容を変える方法について学ぶ。 また、Pythonファイルから関数やクラスを読み込む方法についても学ぶ。

真偽値(TrueFalse)をよく使うため、ブール型や比較演算子について復習しておこう(=== の違いについて覚えているだろうか?)。

if文#

BMI指数を計算して、その値に応じてメッセージを出力するプログラムを作成してみよう。BMIとは、身長と体重の関係から算出される肥満度を表す指数である。 身長を \(a\) cm、体重を \(b\) kgとすると、BMIは \(10000 b / a^2\) と定義される。

a = 193   # 大谷選手の身長、体重
b = 95
bmi = 10000 * b / a ** 2
bmi
25.50404037692287

BMIは18.5未満のとき「低体重」、18.5以上25未満のとき「普通体重」、25以上のとき「肥満」とされる。BMIの値に応じて、これらの情報も添えてメッセージを出力するにはどうしたら良いだろうか?

このように条件をもとに処理内容を変更するときに使われるのがif文である。BMIの例は、if文を使うことで次のように実現できる。

if bmi < 18.5:
    shape = "低体重"
elif bmi < 25:
    shape = "普通体重"
else:
    shape = "肥満"
    
message = f"あなたのBMIは{bmi:.2f}です。これは「{shape}」に当たります。"
print(message)
あなたのBMIは25.50です。これは「肥満」に当たります。

if文の書き方について解説する。上のコードを図で表すと次のようになる。

_images/if.png

Fig. 8 if文の書き方#

条件を表す式のことを条件式と言う。if文では、条件式が真(True)か偽(False)かに基づいて、それに続くブロックを実行するかどうかを決定する。 条件分岐のために使われる構文には ifelifelse の3通りがあり、それぞれ条件式の最後にコロン : を置く。 なお、この授業ではif文という言葉を、if 条件式 という文だけではなく、ifelifelse を含む条件分岐の総称としても用いる。

条件式に続くブロックは、インデントという空白文字による字下げをすることで表現する。空白文字の数はいくつでもよいが、ブロック中はインデントの大きさを揃える必要がある。基本的に、インデントは空白文字4つとするのが良いとされているが[1]、Tabキー1回分をインデントとすることもよく行われる。

if文の処理は、英語読みで理解するとよい。 if 条件式 は「もし条件式が真ならば」という意味である。 elif はelse ifの略であり、elif 条件式 は「もしここまでのブロックを実行しておらず、条件式が真ならば」という意味である。 else は「もしここまでのブロックを実行していないならば」という意味である。

BMIの例では、BMIが18.5未満のとき最初の bmi < 18.5 が真となるため、if文以下の shape = "低体重" が実行される。 BMIが18.5以上25未満のとき次の bmi < 25 で初めて真となるため、elif文以下の shape = "普通" が実行される。 BMIが25以上のとき bmi < 18.5bmi < 25 のどちらも偽となるため、else文以下の shape = "肥満" が実行される。

条件分岐の流れをフローチャートで表現すると次のようになる。

_images/flow.png

Fig. 9 条件分岐の流れ#

いくつか補足する。まず、if文だけの条件分岐を行うこともよくある。その場合はif文に続くブロックを実行するかどうかを、条件式により決定する。 elif文とelse文は必要に応じて追加し、必ずif文、elif文、else文の順番にする。 elif文は好きなだけ追加できるが、else文は1つまでである。

ブロックの中にさらに条件分岐を作ることもできる。 例えば先ほどのBMIの例は、次のように書くこともできる。

if bmi < 18.5:
    shape = "低体重"
else:
    if bmi < 25:
        shape = "普通体重"
    else:
        shape = "肥満"
    
message = f"あなたのBMIは{bmi:.2f}です。これは「{shape}」に当たります。"
print(message)
あなたのBMIは25.50です。これは「肥満」に当たります。

ブロックの内側にブロックを作るときは、インデントを2回行う。 ただしブロックの階層が深くなるにつれてコードが読みづらくなるので、BMIの例では最初のようにelif文を使う方がよい。

練習1
(1) 数値データ x が与えられる。 x が正の値のとき "正の値" と出力するプログラムを書きなさい。

# x の一例 
x = 2

### (出席課題)以下を埋めて、ここの部分のみ提出する。出力にはprint()関数を使うこと。


###

解答例

if x > 0:
    print("正の値")

例えば、以下のように動作する。

  • x = -1 のとき、何も出力しない

  • x = 0 のとき、何も出力しない

  • x = 1 のとき、"正の値" と出力する

誤答例

if x > 0:
    ans = "正の値"
print(ans)

上の例は、x の値によってはエラーとなるため正しくない(明示的に説明していなかったので、採点上は正解とした)。例えば x = -1 のとき、if文のブロックは実行されずにいきなり print(ans) が実行される。このとき ans という変数は未定義なので、エラーが発生する。

次のように書けば問題ない。

if x > 0:
    ans = "正の値"
    print(ans)

また次のようなコードを書いた人もいた。今回は甘めに採点して正解としたが、問題の指示通りではないので本来は誤答である。また、x = 0 のとき "負の値" と出力されてしまうので、その意味でも正しくない。

if x > 0:
    print("正の値")
else:
    print("負の値")

まとめると、問題に書かれていない処理を追加してはいけない。またどのようなケースでもエラーにならないコードを書くこと。

(2) 数値データ x が与えられる。 x が正の値のとき "正の値" 、そうでないとき "0以下の値" と出力するプログラムを書きなさい。

# x の一例 
x = -1

解答例

if x > 0:
    print("正の値")
else:
    print("0以下の値")

例えば、以下のように動作する。

  • x = -1 のとき、"0以下の値" と出力する

  • x = 0 のとき、"0以下の値" と出力する

  • x = 1 のとき、"正の値" と出力する

練習2
2つの数値 ab の最大値を求めて出力するプログラムを、if文を使って作成しなさい。

# a, b の一例 (この例では7と出力するのが正しい)
a = 3
b = 7

解答例

if a > b:
    m = a
else:
    m = b
    
print(m)

例えば、以下のように動作する。

  • a = 3b = 7のとき、7 と出力する

  • a = 8b = 7のとき、8 と出力する

練習3
3つの数値 abc の最大値を求めて出力するプログラムを、if文を使って作成しなさい。

# a, b, c の一例 (この例では7と出力するのが正しい)
a = 3
b = 7
c = 2

解答例

if a > b:
    m = a
else:
    m = b
    
if m < c:
    m = c

print(m)

例えば、以下のように動作する。

  • a = 3b = 7c = 2 のとき、7 と出力する

  • a = 8b = 7c = 2 のとき、8 と出力する

  • a = 3b = 7c = 10 のとき、10 と出力する

式と文

Pythonプログラムは主に式と文で構成される。 式(expression)は、値を生成する構成要素であり、計算や関数の呼び出しがそれに当たる。 一方で文(statement)は、プログラムの実行を制御する構成要素であり、変数への代入や制御構造(if文やfor文など)がそれに当たる。

論理演算子#

複数の条件をもとに条件分岐を行いたい場合がある。 そんなときに用いられるのが論理演算子である。 論理演算子には andornot の3種類がある。 それぞれの意味は名前のとおりではあるが、一通り解説する。

and は、両方の条件が満たされる場合に真を返す。 2つの条件式 AB があったとして、それぞれの真偽と A and B の真偽の関係は次のようになる。

A

B

A and B

True

True

True

True

False

False

False

True

False

False

False

False

or は、少なくとも一つの条件が満たされる場合に真を返す。2つの条件式 AB があったとして、それぞれの真偽と A or B の真偽の関係は次のようになる。

A

B

A or B

True

True

True

True

False

True

False

True

True

False

False

False

not は、真偽を反転する。条件式 A があったとして、その真偽と not A の真偽の関係は次のようになる。

A

not A

True

False

False

True

論理演算子により条件式を組み合わせていくと、算術演算子のときと同じようにどの順で実行するかが問題になる場合がある。 例として、雨が降っているかどうか、傘を持っているかどうかの組み合わせに対して、雨に濡れるかどうかを判定するプログラムを次に示す。

rain = True
umbrella = True

if not rain or umbrella:
    print("雨に濡れません")
else:
    print("雨に濡れます")
雨に濡れません

条件式の not rain or umbrella(not rain) or umbrellanot (rain or umbrella) のどちらの意味だろうか。 上の実行結果をもとに考えてみよう。 このように論理演算子の実行順序がわかりにくい場合には、()をつけてわかりやすい記述を心がけた方がよい。

練習4
文字列 weather と 整数 temp が与えられるとき、以下の条件にもとづいて適切なメッセージを出力するプログラムを作成しなさい。

  • weather"晴れ" かつ temp が25以上なら "暑い日ですね"

  • weather"晴れ" かつ temp が25未満なら "快適な日ですね"

  • weather"雨" なら"傘を持っていきましょう"

# weather, tempの一例
weather = "晴れ"
temp = 17

### (出席課題)以下を埋めて、ここの部分のみ提出する。出力にはprint()関数を使うこと。


###

解答例

if weather == "晴れ" and temp >= 25:
    print("暑い日ですね")
elif weather == "晴れ" and temp < 25:
    print("快適な日ですね")
elif weather == "雨":
    print("傘を持っていきましょう")

練習5
文字列 fruit と リスト basket が与えられるとき、以下の条件にもとづいて適切なメッセージを出力するプログラムを作成しなさい。

  • fruitbasket に含まれないなら "カゴにありません"

ヒント: in演算子を使う

# fruit, basketの一例
fruit = "みかん"
basket = ["りんご", "キウイ", "バナナ"]

解答例

if not fruit in basket:
    print("カゴにありません")

例えば、以下のように動作する。

  • fruit = "みかん"basket = ["りんご", "キウイ", "バナナ"] のとき、"カゴにありません" と出力する

  • fruit = "バナナ"basket = ["りんご", "キウイ", "バナナ"] のとき、何も出力しない

モジュールとimport#

if文から話題を変えて、ここでは新しい関数やクラスをPythonに取り入れるためのimport文について学ぶ。 これまで利用してきた関数やデータ型は、組み込み関数組み込み型と呼ばれ、Pythonに最初から用意されていたものである。 組み込み以外の関数やクラスは、import文によりモジュールからインポートする(読み込む)ことで利用できるようになる。

先にモジュールについて説明する。 Pythonのモジュールとは、関数やクラス、変数などをまとめたPythonファイル(.py)のことを指す。 モジュールは他のPythonプログラムから簡単にインポートして使用することができる。 これにより、コードの再利用性が向上し、プログラムの構造化が可能になっている。 また特定の機能やタスクを実行するために作成されたモジュールの集まりをライブラリという。 Pythonには世界中の開発者により豊富なライブラリが用意されており、Pythonが人気な理由の一つになっている。

さて、モジュールから関数やクラス、変数などを読み込む際に使われるのがimport文である。 基本的な構文は、import モジュール名 である。 例として、randomモジュールをインポートしてみよう。

import random

cand = ["りんご", "キウイ", "バナナ"]
random.choice(cand)
'バナナ'

randomモジュールの中の choice() 関数は、インポート後に random.choice() とすることで使用することができる。 これはリストを引数として受け取り、その要素をランダムに1つ返す関数である。 random.choice() という書き方はメソッドに似ているが、両者は異なる概念なので気をつけよう。

インポートの構文には他にも from モジュール名 import 関数名 のように特定の要素のみインポートする方法(その場合、使用時に モジュール名. は不要になる)や、import モジュール名 as 名前 のようにモジュールの呼び名を自由に設定しながらインポートする方法(その場合 名前.関数名 のような使い方になる)などがある。

ライブラリのインストール

ライブラリは大きく標準ライブラリと外部ライブラリに分けられる。randomモジュールは標準ライブラリの一つである。外部ライブラリの場合にはインポートする前に、そもそもライブラリのファイル群をコンピュータにインストール(ダウンロードし使用可能な状態にすること)する必要がある。よく使われるのはpipというパッケージ管理システムである。pipでライブラリをインストールするには、pip install ライブラリ名 を実行する。

演習#

課題1
じゃんけんの勝敗を判定したい。 自分の手 my_choice、相手の手 opp_choice が与えられるとき、引き分けかどうかを判定して、引き分けのとき "引き分け" と出力するプログラムを作成しなさい。 引き分け以外のときは何も出力しなくてよい。 じゃんけんの手は、"グー""チョキ""パー" の3つである。

# 以下は、my_choice と opp_choiceの一例 (この例では何も出力しないのが正しい)
my_choice = "グー"
opp_choice = "チョキ"

### (出席課題)以下を埋めて、ここの部分のみ提出する。出力にはprint()関数を使うこと。


###

課題2
自分の手 my_choice、相手の手 opp_choice が与えられるとき、自分の勝ちかどうかを判定して、勝ちであれば "勝ち" と出力するプログラムを作成しなさい。それ以外のときは何も出力しなくてよい。

# 以下は、my_choice と opp_choiceの一例 (この例では"勝ち"と出力するのが正しい)
my_choice = "グー"
opp_choice = "チョキ"

課題3
自分の手 my_choice、相手の手 opp_choice が与えられるとき、if文、elif文、else文を使って、勝敗に応じて "勝ち""負け""引き分け" と出力するプログラムを作成しなさい。

# 以下は、my_choice と opp_choiceの一例 (この例では"勝ち"と出力するのが正しい)
my_choice = "グー"
opp_choice = "チョキ"

おまけ
課題3まで出来た人は、作成したプログラムを次のプログラムの末尾にコピー&ペースとして動かしてみよう。 コンピュータと対戦できるように、my_choiceinput() 関数によりユーザーからの入力を受け取るように、opp_choicerandom.choice() 関数により "グー""チョキ""パー" からランダムに選ばれるように変更している。また my_choice"グー""チョキ""パー"のいずれかであることを確認して、そうでない場合にエラーを出力するコードを追加している。

my_choice = input("あなたの選択: ")
opp_choice = random.choice(["グー", "チョキ", "パー"])
print(f"コンピュータの選択: {opp_choice}")

if not my_choice in ["グー", "チョキ", "パー"]:
    raise ValueError("グー、チョキ、パーのいずれかの手を入力してください。") # エラーを出力

# 課題3の答えを以下にコピー&ペーストする

発展的な話題: 例外

raise ValueError() は故意に例外(= エラー)を発生させるための文でraise文と言う。ValueError() は先頭が大文字になっているが、これは関数ではなく、ValueErrorクラスのインスタンスを作成する式である。今回は値が有効ではないことを意味する ValueError を発生させているが、状況ごとに適切な例外のクラスは異なる。はじめのうちは使える必要はないが、このように自分でエラーを発生させる方法もあることは知っておこう。