【入門編】「BMI計算プログラム」を改良して、Pythonの例外処理(try、except、else、finally)を学ぶ

スキルアップ

はじめに

作成したプログラムを実行すると思わぬことでエラーが発生して、処理が強制終了することがあります。

確かに、プログラムを作成する時点で発生し得る全てのパターンを想定しておくことはとても難しいです
つまりエラーとは、想定外の事態においてプログラムに勝手な判断をさせず、処理を停止させる「安全装置」と捉えることもできます。

では、想定されるエラーの場合はどうでしょうか?
前回、作成した「BMI計算プログラム」を例に想定されるエラーへの対処方法を学びます。

例外処理でプログラムを強制終了ではなく、安全に終了させる

「BMI計算プログラム」では、身長と体重を入力させました。
当然、身長と体重は「数字」で入力されることを期待しています。

しかし、身長に「百七十二」と入力されたらどうでしょうか?

もちろん、エラーになります(笑)
※エラーメッセージは、「could not convert string to float: ‘百七十二’」(str型からfloat型に変換できません:’百七十二’)です。

例は少し意地悪に見えますが、入力制限を設けて良いなのであればどのような値が入力されてもおかしくはありません

このように、「エラーになる可能性は想定できるけど、強制終了はさせたくない」場合、例外処理を使用します。

例外処理の記述方法

例外処理は、try、except、else、finallyの4つから出来ています。

関数機能
tryエラーの発生有無を検知するコード
excepttry内でエラーが発生した場合、実行する処理
※複数パターンを設定しても良い
elsetry内が正常に実行された場合、実行する処理
finallytry内の正常/エラーに関わらず、必ず実行する処理
try:
    〜〜〜[処理]〜〜〜

except:
    〜[処理]が「エラー」となった場合のみ実行する処理〜

else:
    〜[処理]が「正常実行」された場合のみ実行する処理〜

finally:
    〜常に実行する処理〜

この例では、どのようなエラーでも「except」が検知します

より具体的に「except」で検知するエラーの種類を指定することもできます。
代表例として、「ゼロ除算」の場合は「ZeroDivisionError」を利用して検知します。
※2つ目の処理は、「ゼロ除算」のエラーではないので「except」で検知しませんでした

さらに、検知したエラーに対するエラーメッセージを利用することもできます。

「except」で検知できる例外は公式ドキュメントに纏まっています。

「BMI計算プログラム」の全体像

#####################
#  [関数①]          #
#    身長と体重を入力  #
#####################
def inputData():
    hight    = float(input("身長は ? cmですか・・・"))
    weight = float(input("体重は ? kgですか・・・"))
    
    return hight, weight

#####################
#  [関数②]       #
#    BMIの計算      #
#####################
def calcBMI(hight, weight):
    bmi = weight / (hight /100) ** 2
    print(f"BMIは、{str(round(bmi, 2))}です")
    
    return bmi

#####################
#  [関数③]       #
#    体型状態の判定   #
#####################
def judgement(bmi):
    if bmi < 18.5:
        judge = "痩せ型"
    elif  bmi >= 18.5 and  bmi < 25:
        judge = "標準"
    elif bmi >= 25:
        judge = "肥満"

    print(f"あなたは、「{judge}」です")

#####################
#  メイン処理       #
#####################
try:
    # [関数①]身長と体重を入力
    hight, weight = inputData()

    # [関数②]BMIの計算
    bmi = calcBMI(hight, weight)
    
except Exception as e:
    print("正しい値を入力してください")
    print(f"Error Message:{e}")

else:
    # [関数③]体型状態の判定
    judgement(bmi)

finally:
    print("BMIの計算を終了します")

プログラムの注目ポイント

例外処理:try句

本プログラムでは、「想定するエラー発生箇所」を2つ選びました。

1つ目は、「身長と体重の入力」に入力に問題があった場合です。
主な原因として、「数値以外を入力してしまう」ことが考えられます。

2つ目は、「BMIの計算」に計算過程に問題があった場合です。
主な原因として、「ゼロ除算」してしまうことが考えられます。

その他の処理は、現時点でエラーとなるパターンが想定できないため、プログラムを強制停止させることとします。

try:
    # [関数①]身長と体重を入力
    hight, weight = inputData()

    # [関数②]BMIの計算
    bmi = calcBMI(hight, weight)

例外処理:except句

try句でエラーが発生した場合、except句に処理を引き継ぎます。
ここでは、「入力値に問題があることを伝えるメッセージ」と「プログラムが出力するエラーメッセージ」を表示します。

今回、「検知するエラーの種類」は任意のエラーを示す「Exception」を指定しました。
※「Exception」は省略可能でしたが、エラーメッセージを利用したかったため、あえて指定することとしました。

except Exception as e:
    print("正しい値を入力してください")
    print(f"Error Message:{e}")
本来は、Exception(任意のエラー)の利用はオススメできません。
なぜなら、どのようなエラーでもelse句に処理を引き継いてしまうと、エラー本来の役割である「安全装置」が機能しなくなるためです。
例外処理を利用するならば、「どのようなエラーを想定するか」を考えるようにしましょう。

例外処理:else句

try句が正常に処理された場合、else句に処理が引き継がれます。

ここでは「BMIの計算」での処理結果「bmi」を利用した、「体型状態の判定」を行います。
float型を想定している「bmi」を利用した条件分岐であり、特段 想定するエラーもありません。
※もしエラーが発生した場合、バグである可能性が高いです。

else:
    # [関数③]体型状態の判定
    judgement(bmi)

例外処理:finally句

try句の正常/エラーに関わらず実行する処理は、finally句に記載します。

今回は、「プログラムを終了を伝えるメッセージ」を表示します。
※他にも「ログファイルの保存」や「本プログラムと連携した別プログラムを正常に終了させる」などにも利用します。

finally:
    print("BMIの計算を終了します")

最後に

プログラムを作成して実行してみると、自分の思い通りに動いてくれないことが多々あります。
そんなとき、何でもかんでもエラーで強制停止してしまうと「しょっちゅう止まるプログラム」になってしまい、使い勝手が非常に悪いです。

想定できるエラーは適切にハンドリングし、不適切な処理を行おうとした場合のみ強制終了させるようなプログラムを目指しましょう!

コメント

タイトルとURLをコピーしました