8.8。 エラーと例外
これまで、エラーメッセージは言及された以上のものではありませんでしたが、例を試してみた場合は、おそらくいくつか見たことがあるでしょう。 (少なくとも)2つの識別可能な種類のエラーがあります:構文エラーと例外。
8.1。 構文エラー
構文エラーは、解析エラーとも呼ばれ、Pythonの学習中に発生する最も一般的な種類の苦情です。
>>> while True print('Hello world')
File "<stdin>", line 1
while True print('Hello world')
^
SyntaxError: invalid syntax
パーサーは問題のある行を繰り返し、エラーが検出された行の最初のポイントを指す小さな「矢印」を表示します。 エラーは、トークン先行矢印によって引き起こされます(または少なくとも検出されます)。この例では、コロン([ X176X] )がその前にありません。 ファイル名と行番号が出力されるので、入力がスクリプトからのものである場合にどこを見ればよいかがわかります。
8.2。 例外
ステートメントまたは式が構文的に正しい場合でも、実行しようとするとエラーが発生する可能性があります。 実行中に検出されたエラーは例外と呼ばれ、無条件に致命的ではありません。Pythonプログラムでエラーを処理する方法をすぐに学習します。 ただし、ほとんどの例外はプログラムによって処理されないため、次のようなエラーメッセージが表示されます。
>>> 10 * (1/0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: division 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: Can't convert 'int' object to str implicitly
エラーメッセージの最後の行は、何が起こったかを示しています。 例外にはさまざまなタイプがあり、そのタイプはメッセージの一部として出力されます。例のタイプは、 ZeroDivisionError 、 NameError 、および TypeError です。 例外タイプとして出力される文字列は、発生した組み込み例外の名前です。 これはすべての組み込み例外に当てはまりますが、ユーザー定義の例外には当てはまる必要はありません(ただし、これは便利な規則です)。 標準の例外名は組み込みの識別子です(予約済みのキーワードではありません)。
行の残りの部分は、例外のタイプとその原因に基づいて詳細を提供します。
エラーメッセージの前の部分は、スタックトレースバックの形式で、例外が発生したコンテキストを示しています。 一般に、ソース行をリストするスタックトレースバックが含まれています。 ただし、標準入力から読み取った行は表示されません。
組み込みの例外には、組み込みの例外とその意味がリストされています。
8.3。 例外の処理
選択した例外を処理するプログラムを作成することができます。 次の例を見てください。有効な整数が入力されるまでユーザーに入力を求めますが、ユーザーはプログラムを中断できます( Control-C またはオペレーティングシステムがサポートするものを使用)。 KeyboardInterrupt 例外を発生させると、ユーザーが生成した割り込みが通知されることに注意してください。
>>> while True:
... try:
... x = int(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
exception 句のクラスは、同じクラスまたはその基本クラスである場合、例外と互換性があります(ただし、その逆ではありません。派生クラスをリストするexcept句は基本クラスと互換性がありません)。 )。 たとえば、次のコードはB、C、Dをこの順序で出力します。
class B(Exception):
pass
class C(B):
pass
class D(C):
pass
for cls in [B, C, D]:
try:
raise cls()
except D:
print("D")
except C:
print("C")
except B:
print("B")
例外句が逆になっている場合(except B
が最初)、B、B、Bが出力されることに注意してください—最初に一致する例外句がトリガーされます。
最後の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句があります。これは、存在する場合、すべてのexcept句の後に続く必要があります。 try句で例外が発生しない場合に実行する必要のあるコードに役立ちます。 例えば:
for arg in sys.argv[1:]:
try:
f = open(arg, 'r')
except OSError:
print('cannot open', arg)
else:
print(arg, 'has', len(f.readlines()), 'lines')
f.close()
else
句を使用すると、 try 句にコードを追加するよりも優れています。これは、 [によって保護されているコードによって発生しなかった例外を誤ってキャッチすることを回避できるためです。 X208X]…except
ステートメント。
例外が発生すると、例外の引数とも呼ばれる値が関連付けられている場合があります。 引数の存在とタイプは、例外タイプによって異なります。
例外句は、例外名の後に変数を指定できます。 変数は、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,
... # but may be overridden in exception subclasses
... x, y = inst.args # unpack args
... print('x =', x)
... print('y =', y)
...
<class 'Exception'>
('spam', 'eggs')
('spam', 'eggs')
x = spam
y = eggs
例外に引数がある場合、それらは未処理の例外のメッセージの最後の部分(「詳細」)として出力されます。
例外ハンドラーは、例外がtry句ですぐに発生する場合だけでなく、try句で(間接的にも)呼び出される関数内で発生する場合にも処理します。 例えば:
>>> def this_fails():
... x = 1/0
...
>>> try:
... this_fails()
... except ZeroDivisionError as err:
... print('Handling run-time error:', err)
...
Handling run-time error: division by zero
8.4。 例外の発生
raise ステートメントを使用すると、プログラマーは指定された例外を強制的に発生させることができます。 例えば:
>>> raise NameError('HiThere')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: HiThere
raise に対する唯一の引数は、発生する例外を示しています。 これは、例外インスタンスまたは例外クラス( Exception から派生するクラス)のいずれかである必要があります。 例外クラスが渡された場合、引数なしでコンストラクターを呼び出すことにより、暗黙的にインスタンス化されます。
raise ValueError # shorthand for 'raise ValueError()'
例外が発生したかどうかを判断する必要があるが、それを処理するつもりがない場合は、 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 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
ほとんどの例外は、標準の例外の名前と同様に、「エラー」で終わる名前で定義されています。
多くの標準モジュールは、定義した関数で発生する可能性のあるエラーを報告するために独自の例外を定義しています。 クラスの詳細については、クラスの章を参照してください。
8.6。 クリーンアップアクションの定義
try ステートメントには、すべての状況で実行する必要があるクリーンアップアクションを定義することを目的とした別のオプションの句があります。 例えば:
>>> try:
... raise KeyboardInterrupt
... finally:
... print('Goodbye, world!')
...
Goodbye, world!
KeyboardInterrupt
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
finally 句が存在する場合、finally
句は、 try ステートメントが完了する前の最後のタスクとして実行されます。 finally
句は、try
ステートメントが例外を生成するかどうかに関係なく実行されます。 次のポイントでは、例外が発生した場合のより複雑なケースについて説明します。
try
句の実行中に例外が発生した場合、その例外は exception 句によって処理される場合があります。 例外がexcept
句によって処理されない場合、finally
句が実行された後に例外が再発生します。except
またはelse
句の実行中に例外が発生する可能性があります。 この場合も、finally
句が実行された後、例外が再発生します。try
ステートメントが break 、 Continue 、または return ステートメントに達すると、finally
句の直前に実行されます。break
、continue
、またはreturn
ステートメントの実行。finally
句にreturn
ステートメントが含まれている場合、戻り値は、try
句のreturn
ステートメント。
例えば:
>>> def bool_return():
... try:
... return True
... finally:
... return False
...
>>> bool_return()
False
より複雑な例:
>>> 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 <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, end="")
このコードの問題は、コードのこの部分の実行が終了した後、ファイルが不確定な時間開いたままになることです。 これは単純なスクリプトでは問題になりませんが、大規模なアプリケーションでは問題になる可能性があります。 with ステートメントを使用すると、ファイルなどのオブジェクトを、常に迅速かつ正確にクリーンアップされるように使用できます。
with open("myfile.txt") as f:
for line in f:
print(line, end="")
ステートメントの実行後、行の処理中に問題が発生した場合でも、ファイル f は常に閉じられます。 ファイルのように、事前定義されたクリーンアップアクションを提供するオブジェクトは、ドキュメントでこれを示します。