Python3エンジニア基礎試験_模擬試験の復習⑧

# エラーと例外について

● エラーの種類

エラーの種類は数多くありますが、基本的には、**構文エラー**と**例外**に分類できます。
**構文エラー**と**例外**について復習していきます。

# 構文エラーとは

構文エラー（SyntaxError）とは、構文が間違っていることで発生するエラーです。

https://docs.python.org/ja/3/library/exceptions.html#SyntaxError

構文が誤っていると、<font color="Red">**SyntaxError**</font>とのエラーが表示されます。

```syntax_error.py
print(
#     print(
#          ^
# SyntaxError: incomplete input
```

また、インデントが不適切な場合も構文エラーが発生します。
インデントが不適切な場合、<font color="Red">**IndentationError**</font>とのエラーが表示されます。

```syntax_error.py
for i in range(5):
print("Hello") # インデントがない
#    print("Hello")
#    ^
# IndentationError: expected an indented block after 'for' statement on line 1
```

# 例外とは

コードを実行した際、構文以外でエラーが検知された場合もエラーが発生することがあります。
この場合に発生するエラーを<font color="Red">**例外**</font>と言います。

**※代表例**

1. <font color="Red">**ZeroDivisionError**</font>

  https://docs.python.org/ja/3/library/exceptions.html#ZeroDivisionError
1. <font color="Red">**NameError**</font>

https://docs.python.org/ja/3/library/exceptions.html#NameError
1. <font color="Red">**TypeError**</font>

https://docs.python.org/ja/3/library/exceptions.html#TypeError
1. <font color="Red">**ValueError**</font>

https://docs.python.org/ja/3/library/exceptions.html#ValueError

```ZeroDivisionError.py
3 / 0
# ZeroDivisionError: division by zero
```

```NameError.py
name ** 2
# NameError: name 'name' is not defined
```

```TypeError.py
"abc" + 3
# TypeError: can only concatenate str (not "int") to str
```

```ValueError.py
int("Hello World")
# ValueError: invalid literal for int() with base 10: 'Hello World'
```

# 例外処理について

● try文

`try`文を使用することで、特定の例外が発生した際の処理を指定できます。

https://docs.python.org/ja/3/reference/compound_stmts.html#the-try-statement

```try.py
while True:
    try:
        x = int(input("数字を入力してください：")) # 検証したいコード
    except ValueError: # 起こりうるエラーの名前
        print("入力に誤りがあります。もう一度入力してください。") # 起こりうるエラーが起きたときの処理
    else:
        break
```

● raise文

`raise`文を使って強制的に例外を発生させることもできます。

https://docs.python.org/ja/3/reference/simple_stmts.html#the-raise-statement

```raise.py
raise TypeError("raise文のテスト")
# TypeError: raise文のテスト 
```

例外が発生した際に特定の処理を実行した後で、再度例外を表示させたい場合は
例外処理の後に`raise`を記述します。例外が発生した際に、ログを記録として残した後で再び例外を出力したい場合などに有効です。

```raise.py
try:
    x = int(input("数字を入力してください：")) # 検証したいコード
except ValueError: # 起こりうるエラーの名前
    print("入力に誤りがあります。もう一度入力してください。") # 起こりうるエラーが起きたときの処理
    raise
# 数字を入力してください：a
# 入力に誤りがあります。もう一度入力してください。
# ValueError: invalid literal for int() with base 10: 'a'
```

※ raise文について、追記予定

# 問題

下記の実行結果を求めよ


```raise.py
print("出力結果:")
try:
  raise Exception("開始前","Exception発生")
  print("開始")
except IOError as msg:
  print("IOError発生:",msg.args[0])
except Exception as msg:
  print("予期せぬ問題発生:",msg.args[1])
else:
  print("Else表示")
# 出力結果:予期せぬ問題発生: Exception発生
```

● 解説

1. 最初に、"出力結果:"という文字列が表示されます。
1. `try`:ブロックが開始されます。
1. `raise Exception("開始前", "Exception発生")`が実行されます。この行により、`Exception`という種類の例外が発生します。例外は引数として2つの文字列を持っています。
1. `except IOError as msg:`に移動しますが、`IOError`の例外は発生していないため、このブロックはスキップされます。
1. `except Exception as msg:`に移動します。ここで発生した例外は、先ほどの`raise`文で発生した`Exception`です。`Exception`の例外はキャッチされ、`msg`という変数に例外の情報が代入されます。
1. `print("予期せぬ問題発生:", msg.args[1])`が実行されます。この行では、例外の第2引数である`"Exception発生"`が表示されます。
1. `else:`ブロックに移動しますが、前の`except`ブロックが実行されたため、このブロックはスキップされます。
1. プログラムの実行が終了します。

※注意点として、<font color="Red">**`raise`文で例外が発生すると、それ以降のコードは実行されず、**</font>例外処理が行われます。そのため、`print("開始")`は実行されずにスキップされます。

```raise.py
try:
    raise NameError("Hello", "World")

except NameError as inst:
    print(inst.args[0])
    raise
# HelloとNameError
```

● 解説

1. `try:`ブロックが開始されます。
1. `raise NameError("Hello", "World")`が実行されます。この行により、`NameError`という種類の例外が発生します。例外は引数として2つの文字列を持っています。
1. `except NameError as inst:`に移動します。ここで発生した例外は、先ほどの`raise`文で発生した`NameError`です。`NameError`の例外はキャッチされ、`inst`という変数に例外の情報が代入されます。
1. `print(inst.args[0])`が実行されます。この行では、例外の第1引数である`"Hello"`が表示されます。
1. `raise`文が再度実行されます。これにより、現在の例外が再度発生します。
1. プログラムの実行が終了します。

```raise.py
try:
    raise ValueError
except ValueError:
  print("Hello World")
# Hello World
```

● 解説

1. `try:`：この行から始まるブロックは、例外が発生する可能性のあるコードを含んでいます。例外が発生すると、それに対応する`except`ブロックが実行されます。
1. `raise ValueError`：この行は、`ValueError`という種類の例外を発生させます。引数を指定せずにValueErrorを発生させたため、エラーメッセージはありません。
1. `except ValueError:`：この行は、`ValueError`という特定の種類の例外が発生した場合に実行されるコードを定義しています。
1. `print("Hello World")`：この行は、`ValueError`が発生した場合に実行されるコードです。`"Hello World"`というメッセージが表示されます。