8. エラーと例外—Pythonドキュメント

提供:Dev Guides
< PythonPython/docs/2.7/tutorial/errors
移動先:案内検索

8.8。 エラーと例外

これまで、エラーメッセージは言及された以上のものではありませんでしたが、例を試してみた場合は、おそらくいくつか見たことがあるでしょう。 (少なくとも)2つの識別可能な種類のエラーがあります:構文エラー例外

8.1。 構文エラー

構文エラーは、解析エラーとも呼ばれ、Pythonの学習中に発生する最も一般的な種類の苦情です。

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

パーサーは問題のある行を繰り返し、エラーが検出された行の最初のポイントを指す小さな「矢印」を表示します。 エラーは、トークン preceding 矢印によって引き起こされます(または少なくとも検出されます)。この例では、コロン(':')がその前にありません。 ファイル名と行番号が出力されるので、入力がスクリプトからのものである場合にどこを見ればよいかがわかります。


8.2。 例外

ステートメントまたは式が構文的に正しい場合でも、実行しようとするとエラーが発生する可能性があります。 実行中に検出されたエラーは例外と呼ばれ、無条件に致命的ではありません。Pythonプログラムでエラーを処理する方法をすぐに学びます。 ただし、ほとんどの例外はプログラムによって処理されないため、次のようなエラーメッセージが表示されます。

>>> 10 * (1/0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero
>>> 4 + spam*3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'spam' is not defined
>>> '2' + 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: cannot concatenate 'str' and 'int' objects

エラーメッセージの最後の行は、何が起こったかを示しています。 例外にはさまざまなタイプがあり、そのタイプはメッセージの一部として出力されます。例のタイプは、ZeroDivisionErrorNameError、およびTypeErrorです。 例外タイプとして出力される文字列は、発生した組み込み例外の名前です。 これはすべての組み込み例外に当てはまりますが、ユーザー定義の例外には当てはまる必要はありません(ただし、これは便利な規則です)。 標準の例外名は組み込みの識別子です(予約済みのキーワードではありません)。

行の残りの部分は、例外のタイプとその原因に基づいて詳細を提供します。

エラーメッセージの前の部分は、スタックトレースバックの形式で、例外が発生したコンテキストを示しています。 一般に、ソース行をリストするスタックトレースバックが含まれています。 ただし、標準入力から読み取った行は表示されません。

組み込みの例外には、組み込みの例外とその意味がリストされています。


8.3。 例外の処理

選択した例外を処理するプログラムを作成することができます。 次の例を見てください。有効な整数が入力されるまでユーザーに入力を求めますが、ユーザーはプログラムを中断できます( Control-C またはオペレーティングシステムがサポートするものを使用)。 KeyboardInterrupt例外を発生させると、ユーザーが生成した割り込みが通知されることに注意してください。

>>> while True:
...     try:
...         x = int(raw_input("Please enter a number: "))
...         break
...     except ValueError:
...         print "Oops!  That was no valid number.  Try again..."
...

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

  • 最初に、 try句try キーワードと except キーワードの間のステートメント)が実行されます。
  • 例外が発生しない場合、 exception節はスキップされ、 try ステートメントの実行が終了します。
  • try句の実行中に例外が発生した場合、残りの句はスキップされます。 次に、そのタイプが exception キーワードにちなんで名付けられた例外と一致する場合、except句が実行され、 try ステートメントの後に実行が続行されます。
  • 例外句で指定された例外と一致しない例外が発生した場合、それは外部の try ステートメントに渡されます。 ハンドラーが見つからない場合は、未処理の例外であり、上記のようなメッセージで実行が停止します。

try ステートメントには、さまざまな例外のハンドラーを指定するために、複数のexcept句を含めることができます。 最大で1つのハンドラーが実行されます。 ハンドラーは、対応するtry句で発生する例外のみを処理し、同じ try ステートメントの他のハンドラーでは処理しません。 例外句は、複数の例外を括弧で囲まれたタプルとして指定できます。次に例を示します。

... except (RuntimeError, TypeError, NameError):
...     pass

except ValueError, e:は、最新のPython(以下で説明)で通常except ValueError as e:と記述される構文であるため、このタプルを括弧で囲む必要があることに注意してください。 古い構文は、下位互換性のために引き続きサポートされています。 これは、except RuntimeError, TypeErrorexcept (RuntimeError, TypeError):と同等ではなく、except RuntimeError as TypeError:と同等であることを意味します。

最後のexcept句では、ワイルドカードとして機能するために、例外名を省略できます。 この方法で実際のプログラミングエラーをマスクするのは簡単なので、これは細心の注意を払って使用してください。 また、エラーメッセージを出力してから、例外を再発生させるためにも使用できます(呼び出し元が例外を処理できるようにします)。

import sys

try:
    f = open('myfile.txt')
    s = f.readline()
    i = int(s.strip())
except IOError as e:
    print "I/O error({0}): {1}".format(e.errno, e.strerror)
except ValueError:
    print "Could not convert data to an integer."
except:
    print "Unexpected error:", sys.exc_info()[0]
    raise

tryexcept ステートメントには、オプションの else句があります。これは、存在する場合、すべてのexcept句の後に続く必要があります。 try句で例外が発生しない場合に実行する必要のあるコードに役立ちます。 例えば:

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()

else 句を使用すると、 try 句にコードを追加するよりも、で保護されているコードによって発生しなかった例外を誤ってキャッチすることが回避されます。 tryexcept ステートメント。

例外が発生すると、例外の引数とも呼ばれる値が関連付けられている場合があります。 引数の存在とタイプは、例外タイプによって異なります。

例外句は、例外名(またはタプル)の後に変数を指定できます。 変数は、instance.argsに格納されている引数を使用して例外インスタンスにバインドされます。 便宜上、例外インスタンスは__str__()を定義しているため、.argsを参照しなくても引数を直接出力できます。

例外を発生させる前に最初に例外をインスタンス化し、必要に応じて属性を追加することもできます。

>>> try:
...     raise Exception('spam', 'eggs')
... except Exception as inst:
...     print type(inst)     # the exception instance
...     print inst.args      # arguments stored in .args
...     print inst           # __str__ allows args to be printed directly
...     x, y = inst.args
...     print 'x =', x
...     print 'y =', y
...
<type 'exceptions.Exception'>
('spam', 'eggs')
('spam', 'eggs')
x = spam
y = eggs

例外に引数がある場合、未処理の例外のメッセージの最後の部分(「詳細」)として出力されます。

例外ハンドラーは、例外がtry句ですぐに発生する場合だけでなく、try句で(間接的にも)呼び出される関数内で発生する場合にも処理します。 例えば:

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

8.4。 例外の発生

raise ステートメントを使用すると、プログラマーは指定された例外を強制的に発生させることができます。 例えば:

>>> raise NameError('HiThere')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: HiThere

raise に対する唯一の引数は、発生する例外を示しています。 これは、例外インスタンスまたは例外クラス(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 <module>
NameError: HiThere

8.5。 ユーザー定義の例外

プログラムは、新しい例外クラスを作成することにより、独自の例外に名前を付けることができます(Pythonクラスの詳細については、 Classes を参照してください)。 例外は通常、直接または間接的に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 <module>
__main__.MyError: 'oops!'

この例では、Exceptionのデフォルトの__init__()がオーバーライドされています。 新しい動作では、 value 属性が作成されるだけです。 これは、 args 属性を作成するデフォルトの動作に置き換わるものです。

例外クラスは、他のクラスが実行できることをすべて実行するように定義できますが、通常は単純に保たれ、多くの場合、例外のハンドラーがエラーに関する情報を抽出できるようにするいくつかの属性のみを提供します。 いくつかの異なるエラーを発生させる可能性のあるモジュールを作成する場合、一般的な方法は、そのモジュールによって定義された例外の基本クラスを作成し、そのサブクラスを作成して、さまざまなエラー条件の特定の例外クラスを作成することです。

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

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

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

    def __init__(self, expr, msg):
        self.expr = expr
        self.msg = msg

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

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

    def __init__(self, prev, next, msg):
        self.prev = prev
        self.next = next
        self.msg = msg

ほとんどの例外は、標準の例外の名前と同様に、「エラー」で終わる名前で定義されています。

多くの標準モジュールは、定義した関数で発生する可能性のあるエラーを報告するために独自の例外を定義しています。 クラスの詳細については、クラスの章を参照してください。


8.6。 クリーンアップアクションの定義

try ステートメントには、すべての状況で実行する必要があるクリーンアップアクションを定義することを目的とした別のオプションの句があります。 例えば:

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

final句は、例外が発生したかどうかに関係なく、 try ステートメントを終了する前に常に実行されます。 try 句で例外が発生し、 exception 句で処理されなかった場合(または exception または else [ X165X]句)、 finally 句が実行された後に再発生します。 finally 句は、 try ステートメントの他の句が breakContinue を介して残された場合にも、「途中で」実行されます。 ]または return ステートメント。 より複雑な例(同じ try ステートメントに except 句と finally 句があると、Python 2.5の時点で機能します):

>>> 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
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 <module>
  File "<stdin>", line 3, in divide
TypeError: unsupported operand type(s) for /: 'str' and 'str'

ご覧のとおり、 finally 句はどのような場合でも実行されます。 2つの文字列を分割して発生したTypeErrorは、 exception 句では処理されないため、 finally 句の実行後に再発生します。

実際のアプリケーションでは、 finally 句は、リソースの使用が成功したかどうかに関係なく、外部リソース(ファイルやネットワーク接続など)を解放するのに役立ちます。


8.7。 事前定義されたクリーンアップアクション

一部のオブジェクトは、オブジェクトを使用した操作が成功したか失敗したかに関係なく、オブジェクトが不要になったときに実行される標準のクリーンアップアクションを定義します。 次の例を見てください。この例では、ファイルを開いてその内容を画面に出力しようとしています。

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

このコードの問題は、コードの実行が終了した後、ファイルが不確定な時間開いたままになることです。 これは単純なスクリプトでは問題になりませんが、大規模なアプリケーションでは問題になる可能性があります。 with ステートメントを使用すると、ファイルなどのオブジェクトを、常に迅速かつ正確にクリーンアップされるように使用できます。

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

ステートメントの実行後、行の処理中に問題が発生した場合でも、ファイル f は常に閉じられます。 事前定義されたクリーンアップアクションを提供する他のオブジェクトは、ドキュメントでこれを示します。