はじめに
pythonで『例外クラスを自作する方法』、『例外ごとに定型(変数を含む)のエラー内容を返す方法』を調べたので、ここに整理しようと思います。
ゴール
4桁の数字を与えて、それが正しい月日の表現になっているかチェックするコードを作りました。
4桁の数字ではなかったり、実在しない月日(13月など)になっている場合にエラー内容を出力することが目的です。
※ 今回は1月~12月の全てが31日まであることとしています(そこは今回は重要ではないので)
###正しい月日を指定した場合
$ python main.py 0101
指定した日付は01月01日です。
$ python main.py 1231
指定した日付は12月31日です。
###誤った月日を指定した場合
$ python main.py 12345
[12345]は4桁の数字ではありません。例:1月2日なら0102の形式で指定してください。
$ python main.py 1301
[13]は月ではありません。01~12の値を指定してください。
$ python main.py 0132
[32]は日ではありません。01~31の値を指定してください。
自作の例外クラス
まず例外クラスを準備します。
今回は
・4桁の数字でない場合
・月が01-12でない場合
・日が01-31でない場合
の3つのパターンを用意しますが、いずれもエラー内容とエラーになった部分(引数)を出力したいので、MyException
クラスを用意して例外インスタンス生成に属性として引数を格納するようにしています。
__str__関数はインスタンスにstrやprintが適用されたときにstringオブジェクトを返す特殊メソッドで、これをoverrideしてエラーメッセージを返すようにしています。
class MyException(Exception):
def __init__(self, arg=""):
self.arg = arg
class InvalidInputException(MyException):
def __str__(self):
return (
f"[{self.arg}]は4桁の数字ではありません。例:1月2日なら0102の形式で指定してください。"
)
class InvalidMonthException(MyException):
def __str__(self):
return (
f"[{self.arg}]は月ではありません。01~12の値を指定してください。"
)
class InvalidDayException(MyException):
def __str__(self):
return (
f"[{self.arg}]は日ではありません。01~31の値を指定してください。"
)
(参考: 8.3. 例外を処理する)
ちなみに、例外インスタンスでは__str__
関数はインスタンスの引数を直接印字できるよう実装されているそうです。
except 節では、例外名の後に変数を指定することができます。この変数は例外インスタンスに結び付けられており、 instance.args に例外インスタンス生成時の引数が入っています。例外インスタンスには str() が定義されており、 .args を参照しなくても引数を直接印字できるように利便性が図られています。必要なら、例外を送出する前にインスタンス化して、任意の属性を追加できます。
メイン処理
ポイントはtry~except
の箇所かと思います。
Exceptionごとに定型のメッセージを出力するのは共通なので、Exceptionごとに補足時の処理を実装するよりも簡潔になります。
また、もし他の箇所でこれらのExceptionが使われる場合も同じように書けるので、メンテしやすいのかなと思います。
import re
import sys
def main():
# 引数が4桁の数字であるかチェック
arg = sys.argv[1]
check_arg(arg)
# 引数を2桁ごとに分けて、月日であるかチェック
month = arg[0:2]
day = arg[2:4]
check_month(month)
check_day(day)
print(f"指定した日付は{month}月{day}日です。")
def check_arg(arg):
if not re.match("^\d{4}$", arg):
raise InvalidInputException(arg)
def check_month(month):
if not re.match("^0[1-9]|1[0-2]$", month):
raise InvalidMonthException(month)
def check_day(day):
if not re.match("^0[1-9]|[12][0-9]|3[01]$", day):
raise InvalidDayException(day)
if __name__ == "__main__":
try:
main()
except(
InvalidInputException,
InvalidMonthException,
InvalidDayException,
) as e:
print(e)
参考
参考にさせていただきました
Qiita: Pythonで例外を投げるときのベストプラクティス
Python 入門: Python のユーザー定義例外クラス
note: pythonでスマートに例外処理をする