初めてのPython実践試験学習 第19回「except節でエラーを正しく把握する」

こんにちは、吉政創成 菱沼です。

今回もPythonエンジニア育成推進協会のPython 3 エンジニア認定実践試験の主教材「Python実践レシピ/技術評論社」を使って学習中です。

前回は、独自例外処理について学びました。

今回は、よくあるエラーとその対処法について学びます。

try-exceptの注意点

今回はテキストの3.1.6になります。(3.1.5は第17回で取り上げた内容のため、割愛します。)

ではまずは引用から。

—————————

P.45

3.1.6 例外処理:よくあるエラーと対処法

一般にtry文で囲む範囲は狭いほうがよいとされています。範囲が広いとtry節が何を意図しているのか理解することが困難になり、可読性が下がります。

また、except節で例外の種類を指定しなかったり、except Exception:のように基底クラスを指定してしまうのも推奨されません。想定外の例外を許容したままプログラムの実行が継続される恐れがあり、エラー―原因の特定が難しくなるためです。

—————————

except節を書く際、[except Exception:]とすると、通常のエラーのほとんどを捕捉できますが、対象が広すぎるため、本来発生に気づいて対処すべき例外まで握りつぶしてしまう可能性があります。

また、対象を指定せずに[except:]とだけ書いた場合、BaseExceptionを捉まえるのと同じ動作をします。これはZeroDivisionErrorやValueErrorといった一般的な例外だけでなく、プログラムを終了させるために使われる SystemExit や、ユーザーの中断操作である KeyboardInterrupt などまでまとめて捕まえてしまいます。

その結果、本来はプログラムを停止させるべき状況まで握りつぶされ、異常状態のまま処理が継続してしまうという恐れがあります。

なので、実務の現場では、具体的な例外名を付け加えて書く事が推奨されています。

例:except ZeroDivisionError: except FileNotFoundError: など

例外のクラス階層については第17回のコラムで学びましたので、振り返りたい方はそちらをどうぞ。

初めてのPython実践試験学習 第17回「Pythonの言語仕様:例外クラスの構造と捕捉」

適切に例外を指定して処理するには

という前提のもと、サンプルコードを見てみます。

このサンプルコードでは都道府県の人口と面積が書かれたcsvファイルを読み込んで人口密度を計算して表示するという動作を行うものになっています。

—————————

P.44

sample.csv

“東京都”,”13900000″,”1a”   #設定値に文字列が紛れ込んでしまっている
“神奈川県”,”9200000″,”2616.10″
“千葉県”,”6200000″,”5157.50″
“埼玉県”,”7300000″,”3797.75″

悪い例

import csv

try:
    input_file_name = input(‘Enter file_name:’)
    with open(input_file_name, mode=’r’) as f:
        reader = csv.reader(f)
        for row in reader:
            population_desity = float(row[1]) / float(row[2])
            print(f'{row[0]}の人口密度は{population_desity}です’)

except:
    print(‘例外が発生しました’)

—————————

この例では例外の指定を全くしていないので、発生するすべての例外が同じメッセージとして処理されてしまいます。そのせいで、csvファイル の 1 行目に “1a” という不正な値があるにもかかわらず、どのような例外が起きたのか判断することができません。以下は実行結果です。

コードの実行結果)

そこで、想定される代表的な例外を個別に捕捉するように修正します。

・ValueError: 引数の値が不適切
・FileNotFoundError: ファイルが見つからない
・ZeroDivisionError:  ゼロ除算エラー

これを指定することで、個別に処理ができ、例外の特定が容易になります。

修正されたサンプルコードはこちら。変わった場所は太字にしています。

import csv

input_file_name = input(‘Enter file_name:’)
try:
    with open(input_file_name, mode=’r’) as f:
        reader = csv.reader(f)
        for row in reader:
            population_desity = float(row[1]) / float(row[2])
            print(f'{row[0]}の人口密度は{population_desity}です’)
except FileNotFoundError:
    print(f’ファイルがありません’)
except ZeroDivisionError:
    print(f'{row[0]}:{row[1]},{row[2]} => 値0は指定できません’)
except ValueError:
    print(f'{row[0]}:{row[1]},{row[2]} => 数値以外は指定できません’)

これを実行すると次の結果が表示されます。

ところで、except節の修正以外にinput_file_nameがtry文の外に移動しています。

try の中は「本当に例外処理したい最小限の処理」だけにとどめるのが望ましいため、ファイル名を入力する時の問題(例:EOFError など)まで FileNotFoundError として扱わないようにするため、外に出されたということだと思います。

今回はこちらで終了です。お付き合いいただきありがとうございました。

実践試験について知りたい方は以下をご覧ください。

●Python3エンジニア認定実践試験

PAGE TOP