Python 3 エラーと例外

Pythonの初心者として、最初にPythonプログラミングを学習する場合、エラーメッセージがよく表示されます。この章では、それらのメッセージを説明します。

Pythonには、構文エラーと例外の2種類のエラーに分けます。

Python assertは、式を判断するために使用され、式の条件がfalseの場合に例外をトリガーします。

構文エラー

次の例のように、Pythonの構文エラー(解析エラーとも言えます)は、初心者がよく遭います。

>>> while True print('Hello world')
  File "<stdin>", line 1, in ?
    while True print('Hello world')
                   ^
SyntaxError: invalid syntax

この例では、関数print()にはエラーが表示され、前にコロンがありません。

パーサは間違った行を指摘し、エラーが最初に見つかった場所に小さな矢印をマークしました。

例外

Pythonプログラムの構文が正しいですが、実行時にエラーが発生する可能性があります。実行時に検出されたエラーは、例外と呼ばれます。

ほとんどの例外はプログラムによって処理されず、エラーメッセージの形式で表示されます。

実例

>>> 10 * (1/0)             #0は除数として使用できず、例外がトリガーされます
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
ZeroDivisionError: division by zero
>>> 4 + spam*3             # spamは定義されていません。例外がトリガーされます
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
NameError: name 'spam' is not defined
>>> '2' + 2               #intはstrと加算できず、例外がトリガーされます
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only concatenate str (not "int") to str

その一部がプリントされます。例にあるエラーは次のいくつかの種類があります。ZeroDivisionError、NameError、およびTypeErrorです。

エラーメッセージの前部には、例外が発生したメッセージが表示され、具体的な情報がスタックトレースの形式で表示されます。

例外処理

try/except

例外処理には、 try/exceptステートメントを使用できます。

次の例では、ユーザーは有効な整数を入力し、プログラムを中断できます。(Control-Cまたはオペレーティングシステムが提供する方法を使用します)中断のメッセージによって、KeyboardInterrupt例外が発生します。

while True:
    try:
        x = int(input("1つの数字を入力してください:"))
        break
    except ValueError:
        print("入力したのは数字ではありません。もう一度入力してください。") 

tryステートメントは次のように実行します。

  • まず、tryサブステートメント(キーワードtryとキーワードexceptの間のステートメント)を実行します。
  • 例外が発生しない場合は、exceptサブステートメントを無視し、tryサブステートメントが実行された後に終了します。
  • tryサブステートメントの実行中に例外が発生した場合、tryサブステートメントの残った部分は無視されます。例外のタイプがexceptの後の名前と一致する場合、対応するexceptサブステートメントが実行されます。
  • 例外が任意のexceptと一致しない場合、例外は上位の tryに渡されます。

tryステートメントには、複数のexceptサブステートメントを含めることが可能で、異なる例外を処理します。最大で1つのブランチのみが実行されます。

処理プログラムは、対応するtryサブステートメントの例外のみを処理し、他のtry処理プログラムの例外は処理しません。

exceptサブステートメントは、複数の例外を同時に処理できます。これらの例外は、タプルとして括弧内に配置されます。次の例に示します。

except (RuntimeError, TypeError, NameError):
    pass

最後のexceptサブステートメントは、例外の名前を無視できます。ワイルドカードとして使用されます。このメソッドを使用してエラーメッセージを出力してから、例外を再スローできます。

import sys

try:
    f = open('myfile.txt')
    s = f.readline()
    i = int(s.strip())
except OSError as err:
    print("OS error: {0}".format(err))
except ValueError:
    print("Could not convert data to an integer.")
except:
    print("Unexpected error:", sys.exc_info()[0])
    raise

try/except…else

try / exceptionステートメントにはオプションのelseサブステートメントもあります。このサブステートメントを使用する場合、すべてのexceptサブステートメントの後に配置する必要があります。

elseサブステートメントは、tryサブステートメントで例外が発生しなかった時に実行します。

次の例では、tryステートメントでファイルを開くことができるかどうかを判定し、ファイルを開く時に異常がない場合は、else部分のステートメントを実行してファイルの内容を読み取ります。

for arg in sys.argv[1:]:
    try:
        f = open(arg, 'r')
    except IOError:
        print('cannot open', arg)
    else:
        print(arg, 'has', len(f.readlines()), 'lines')
        f.close()

すべてのステートメントをtryサブステートメントに配置するよりも、elseサブステートメントを使用することをお勧めします。これより、exceptがキャッチできない予想外の例外を回避できます。

例外処理は、tryサブステートメントで直接発生する例外を処理するだけでなく、サブステートメントで呼び出される関数(間接的に呼び出される関数でも含む)でスローされる例外も処理できます。例えば:

>>> def this_fails():
        x = 1/0
   
>>> try:
        this_fails()
    except ZeroDivisionError as err:
        print('Handling run-time error:', err)
   
Handling run-time error: int division or modulo by zero

try-finally ステートメント

try-finallyステートメントは、例外が発生したかどうかに関係なく、最終のコードを実行します。

次の例にあるfinallyステートメントは、例外が発生したかどうかに関係なく、実行します。

実例

try:
    runoob()
except AssertionError as error:
    print(error)
else:
    try:
        with open('file.log') as file:
            read_data = file.read()
    except FileNotFoundError as fnf_error:
        print(fnf_error)
finally:
    print('例外が発生したかどうかに関係なく、実行します。')

例外をスローする

Pythonは、raiseステートメントを使用して、指定された例外を発生させます。

raiseの構文は次のとおりです。

raise [Exception [, args [, traceback]]]

次の例では、x>5の場合は例外をトリガーします。

x = 10
if x > 5:
    raise Exception('xは大なり5にすることはできません。 xの値は: {}'.format(x))

上記のコードを実行すると、例外がトリガーされます。

Traceback (most recent call last):
  File "test.py", line 3, in <module>
    raise Exception('xは大なり5にすることはできません。 xの値は: {}'.format(x))
Exception: xは大なり5にすることはできません。 xの値は: 10

する例外を指定します。 例外または例外クラス(つまり、Exceptionのサブクラス)である必要があります。

これが例外をスローするかどうかを知りたいだけで、それを処理したくない場合は、raiseステートメントによって再スローできます。

>>> try:
        raise NameError('HiThere')
    except NameError:
        print('An exception flew by!')
        raise
   
An exception flew by!
Traceback (most recent call last):
  File "<stdin>", line 2, in ?
NameError: HiThere

ユーザー定義の例外

新しい例外クラスクラスを作成することによって、独自の例外を作成できます。例外クラスは、Exceptionクラスから継承し、直接継承するか、間接的に継承するか、両方もできます。例えば:

>>> class MyError(Exception):
        def __init__(self, value):
            self.value = value
        def __str__(self):
            return repr(self.value)
   
>>> try:
        raise MyError(2*2)
    except MyError as e:
        print('My exception occurred, value:', e.value)
   
My exception occurred, value: 4
>>> raise MyError('oops!')
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
__main__.MyError: 'oops!'

この例では、クラスExceptionのデフォルトの __init__()がオーバーライドされます。

モジュールを作成すると複数の異なる例外がスローされる可能性がある場合、通常、このモジュールで例外クラスを作成し、この例外クラスに基づいてさまざまなエラーに対してさまざまなサブクラスを作成できます。

class Error(Exception):
    """Base class for exceptions in this module."""
    pass

class InputError(Error):
    """Exception raised for errors in the input.

    Attributes:
        expression -- input expression in which the error occurred
        message -- explanation of the error
    """

    def __init__(self, expression, message):
        self.expression = expression
        self.message = message

class TransitionError(Error):
    """Raised when an operation attempts a state transition that's not
    allowed.

    Attributes:
        previous -- state at beginning of transition
        next -- attempted new state
        message -- explanation of why the specific transition is not allowed
    """

    def __init__(self, previous, next, message):
        self.previous = previous
        self.next = next
        self.message = message

ほとんどの例外名は、標準の例外名と同じように、”Error”で終わります。

クリーンアップ動作を定義する

tryステートメントには、別のオプションのサブステートメントがあり、任意の状況で実行されるクリーンアップ動作を定義します。例えば:

>>> try:
...     raise KeyboardInterrupt
... finally:
...     print('Goodbye, world!')
... 
Goodbye, world!
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
KeyboardInterrupt

上記の例では、tryサブステートメントで例外が発生するかどうかに関係なく、finallyサブステートメントが実行されます。

tryサブステートメント(またはexceptとelseサブステートメント)で例外がスローされ、任意のexceptがそれをキャッチしなかった場合、この例外はfinallyサブステートメントが実行された後にスローされます。

より複雑な例を次に挙げます。(同じtryステートメントのexceptとfinallyサブステートメントを含む)

>>> def divide(x, y):
        try:
            result = x / y
        except ZeroDivisionError:
            print("division by zero!")
        else:
            print("result is", result)
        finally:
            print("executing finally clause")
   
>>> divide(2, 1)
result is 2.0
executing finally clause
>>> divide(2, 0)
division by zero!
executing finally clause
>>> divide("2", "1")
executing finally clause
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "<stdin>", line 3, in divide
TypeError: unsupported operand type(s) for /: 'str' and 'str'

事前に定義されたクリーンアップ動作

一部のオブジェクトは、システムが正常に使用したかどうかに関係なく、標準のクリーンアップ動作を定義します。不要になると、標準のクリーンアップ動作が実行されます。

この例では、ファイルを開いてから内容を画面に出力して試みます。

for line in open("myfile.txt"):
    print(line, end="")

上記のコードの問題は、実行が完了した後、ファイルが開いたまま、閉じられないことです。

キーワードwithステートメントを使用すると、ファイルなどのオブジェクトが使用された後、クリーンアップメソッドが正しく実行できます。

with open("myfile.txt") as f:
    for line in f:
        print(line, end="")

上記のコードを実行した後、処理過程中に問題が発生しても、ファイルfは閉じられます。

Share

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です