Pythonのエラー処理を詳しく解説

WBOY
リリース: 2016-06-16 08:41:39
オリジナル
1507 人が閲覧しました

プログラムの実行中にエラーが発生した場合、エラーコードを返すことに事前に同意することで、エラーの有無とエラーの原因を知ることができます。オペレーティング システムが提供する呼び出しによってエラー コードが返されるのは非常に一般的です。たとえば、ファイルを開く関数 open() は、成功した場合はファイル記述子 (整数) を返し、エラーが発生した場合は -1 を返します。

エラーが発生したかどうかを示すためにエラー コードを使用するのは非常に不便です。関数自体が返す通常の結果がエラー コードと混合され、呼び出し元がエラーが発生したかどうかを判断するために大量のコードを使用することになるためです。 :

コードをコピー コードは次のとおりです:

def foo():
r = some_function()
r==(-1) の場合:
リターン (-1)
# 何かをする
rを返します

def bar():
r = foo()
r==(-1) の場合:
print 'エラー'
それ以外:
パス


エラーが発生すると、関数がエラーを処理できるようになるまで (たとえば、ユーザーにエラー メッセージを出力するなど)、エラーをレベルごとに報告する必要があります。

高級言語には通常、try...excel...finally... というエラー処理メカニズムが組み込まれており、Python も例外ではありません。

試してください

例を使用して try のメカニズムを見てみましょう:

コードをコピー コードは次のとおりです:

試してみてください:
「試してみてください...」を印刷
r = 10 / 0
'結果:'を印刷、r
ZeroDivisionError を除く、e:
'以外:'、e
を印刷します 最後に:
「ついに...」を印刷
「終了」を印刷

一部のコードが間違っている可能性があると思われる場合は、このコードを実行してみます。エラーが発生した場合、後続のコードは実行を続行せず、エラー処理コード、つまり、Except ステートメント ブロックに直接ジャンプします。 . 実行後、finally ステートメント ブロックがある場合は、この時点で実行が完了します。

上記のコードは、10 / 0 を計算するときに除算エラーを生成します。

コードをコピー コードは次のとおりです:

試してみてください...
例外: 整数の除算またはゼロによるモジュロ
ついに…
終了

出力からわかるように、エラーが発生すると、後続のステートメント print 'result:', r は実行されません。ただし、ZeroDivisionError がキャプチャされるため実行されます。最後に、finally ステートメントが実行されます。その後、プログラムは引き続きプロセスを追跡します。

除数 0 を 2 に変更すると、実行結果は次のようになります。

コードをコピー コードは次のとおりです:

試してみてください...
結果: 5
ついに…
終了

エラーが発生しないため、Except 文ブロックは実行されませんが、finally 文があれば必ず実行されます (finally 文がない場合もあります)。

また、さまざまなタイプのエラーが発生する場合、それらは別の else ステートメント ブロックで処理されるはずであると推測できます。そうです、さまざまなタイプのエラーをキャッチするために複数の例外が存在する可能性があります:

コードをコピー コードは次のとおりです:

試してみてください:
「試してみてください...」を印刷
r = 10 / int('a')
'結果:'を印刷、r
ValueError を除く、e:
Print 'ValueError:', e
ZeroDivisionError を除く、e:
Print 'ZeroDivisionError:', e
最後に:
「ついに...」を印刷
「終了」を印刷

int() 関数は ValueError をスローする可能性があるため、ValueError のキャッチ以外に 1 つを使用し、ZeroDivisionError のキャッチ以外にもう 1 つを使用します。

さらに、エラーが発生しない場合は、Except ステートメント ブロックの後に else ステートメントを追加できます。エラーが発生しない場合、else ステートメントは自動的に実行されます。

コードをコピー コードは次のとおりです:
試してみてください:
「試してみてください...」を印刷
r = 10 / int('a')
'結果:'を印刷、r
ValueError を除く、e:
Print 'ValueError:', e
ZeroDivisionError を除く、e:
Print 'ZeroDivisionError:', e
それ以外:
「エラーはありません!」を印刷します
最後に:
「ついに...」を印刷
「終了」を印刷

Python エラーは実際にはクラスであり、すべてのエラー タイプは BaseException を継承します。そのため、Except を使用する場合は、このタイプのエラーをキャプチャするだけでなく、そのサブクラスも「すべてキャッチ」するという事実に注意する必要があります。例:

コードをコピー コードは次のとおりです:

試してみてください:
foo()
StandardError を除く、e:
'StandardError' を印刷します
ValueError を除く、e:
'ValueError' を印刷します

ValueError は StandardError のサブクラスであるため、2 番目の例外は決してキャッチしません。存在する場合は、最初の例外によってキャッチされます。

Python のすべてのエラーは BaseException クラスから派生します。一般的なエラーの種類と継承関係については、こちらを参照してください。
https://docs.python.org/2/library/Exceptions.html#Exception-hierarchy

エラーをキャッチするために try...以外を使用するもう 1 つの大きな利点は、関数 main() が foo() を呼び出し、foo() が bar() を呼び出すと、エラーが発生するということです。現時点では、 main() がそれをキャプチャしている限り、それを処理できます:

コードをコピー コードは次のとおりです:

def foo(s):
10 / int(s) を返します

デフォルトバー:
foo(s) * 2

を返します

def main():
試してみてください:
bar('0')
StandardError を除く、e:
print 'エラー!'
最後に:
print 'ついに...'


言い換えれば、問題が発生する可能性のあるすべての場所でエラーを検出する必要はなく、適切なレベルでエラーを検出するだけで十分です。このようにして、try...exciting...finally を記述する手間が大幅に軽減されます。

コールスタック

エラーが捕捉されない場合は、エラーがスローされ、最終的に Python インタープリターによって捕捉され、エラー メッセージが出力されて、プログラムが終了します。 err.py を見てみましょう:

コードをコピー コードは次のとおりです:

# err.py:
def foo(s):
10 / int(s) を返します

デフォルトバー:
foo(s) * 2

を返します

def main():
bar('0')

メイン()


実行すると、結果は次のようになります:
コードをコピー コードは次のとおりです:

$ python err.py
トレースバック (最後の呼び出し):
ファイル「err.py」、11 行目、
main()
ファイル「err.py」、メイン
の 9 行目 bar('0')
ファイル「err.py」、行 6、bar
foo(s) * 2
を返します ファイル「err.py」、foo
の 3 行目 10 / int(s) を返します
ZeroDivisionError: 整数の除算またはゼロによるモジュロ

間違いを犯すことは悪いことではありません。恐ろしいのは、どこで間違ったのかがわからないことです。エラー メッセージの解釈は、エラーを特定するための鍵となります。間違った呼び出し関数チェーン全体が上から下まで確認できます:

エラーメッセージ行 1:

コードをコピー コードは次のとおりです:

トレースバック (最後の呼び出し):

これは不正な追跡情報であると伝えてください。

2 行目:

コードをコピー コードは次のとおりです:

ファイル「err.py」、11 行目、
main()

コード ファイル err.py の 11 行目で main() を呼び出すときにエラーが発生しましたが、理由は 9 行目です:
コードをコピー コードは次のとおりです:

ファイル「err.py」、メイン
の 9 行目 bar('0')

コード ファイル err.py の 9 行目で bar('0') を呼び出すときにエラーが発生しましたが、その理由は 6 行目です:
コードをコピー コードは次のとおりです:

ファイル「err.py」、行 6、bar
foo(s) * 2
を返します
その理由は、ステートメント return foo(s) * 2 が間違っているためですが、これが最終的な理由ではありません。
コードをコピー コードは次のとおりです:
ファイル「err.py」、foo
の 3 行目 10 / int(s) を返します

その理由は、ステートメント return 10 / int(s) にエラーがあるためです。以下に表示されているため、これがエラーの原因です。

コードをコピー コードは次のとおりです: ZeroDivisionError: 整数の除算またはゼロによるモジュロ


エラーの種類 ZeroDivisionError から、int(s) 自体にはエラーがないと判断しますが、int(s) は 0 を返し、10 / 0 の計算時にエラーが発生します。この時点でエラーの原因が判明します。
ログエラー

エラーが捕捉されなかった場合、Python インタプリタは当然エラー スタックを出力できますが、プログラムも終了します。エラーを捕捉できたので、エラー スタックを出力し、エラーの原因を分析し、同時にプログラムの実行を続行できます。

Python の組み込みログ モジュールは、エラー メッセージを簡単に記録できます:

コードをコピー コードは次のとおりです:

# err.py
インポートログ

def foo(s):
10 / int(s) を返します

デフォルトバー:
foo(s) * 2

を返します

def main():
試してみてください:
bar('0')
StandardError を除く、e:
logging.Exception(e)

メイン()
「終了」を印刷


同じエラーが発生しますが、プログラムはエラー メッセージを出力した後も実行を継続し、正常に終了します:
コードをコピー コードは次のとおりです:

$ python err.py
エラー: ルート: 整数の除算またはゼロによるモジュロ
トレースバック (最後の呼び出し):
ファイル「err.py」、メイン
の 12 行目 bar('0')
ファイル「err.py」、8 行目、bar
foo(s) * 2
を返します ファイル「err.py」、5 行目、foo
10 / int(s) を返します
ZeroDivisionError: 整数の除算またはゼロによるモジュロ
終了

構成を通じて、ロギングによってエラーをログ ファイルに記録し、その後のトラブルシューティングを容易にすることもできます。

エラーがスローされます

エラーはクラスであるため、エラーをキャッチすることはクラスのインスタンスをキャッチすることを意味します。したがって、エラーは何もないところから発生するのではなく、意図的に作成されてスローされます。 Python の組み込み関数はさまざまな種類のエラーをスローする可能性があり、自分で作成した関数もエラーをスローする可能性があります。

エラーをスローする場合は、まず必要に応じてエラー クラスを定義し、継承関係を選択してから、raise ステートメントを使用してエラー インスタンスをスローします。

コードをコピー コードは次のとおりです:

# err.py
class FooError(StandardError):
パス

def foo(s):
n = int(s)
n==0 の場合:
Raise FooError('無効な値: %s' % s)
10 / n を返します


実行すると、最終的に独自に定義したエラーを追跡できます:
コードをコピー コードは次のとおりです:

$ python err.py
トレースバック (最後の呼び出し):
...
__main__.FooError: 無効な値: 0

必要な場合にのみ独自のエラー タイプを定義します。 Python の組み込みエラー タイプ (ValueError、TypeError など) を選択できる場合は、Python の組み込みエラー タイプを使用してみてください。

最後に、エラー処理の別の方法を見てみましょう:

コードをコピー コードは次のとおりです:

# err.py
def foo(s):
n = int(s)
10 / n を返します

デフォルトバー:
試してみてください:
return foo(s) * 2
StandardError を除く、e:
print 'エラー!'
レイズ

def main():
bar('0')

メイン()


bar() 関数では明らかにエラーをキャッチしていますが、Error! を出力した後、raise ステートメントを通じてエラーをスローしています。これは不健全ではないでしょうか。

実際、このエラー処理方法は無害であるだけでなく、非常に一般的でもあります。エラーをキャプチャする目的は、後の追跡のためにエラーを記録することだけです。ただし、現在の関数はエラーを処理する方法を知らないため、最も適切な方法はエラーをスローし続け、最上位の呼び出し元に処理させることです。

raise ステートメントがパラメータを取らない場合、現在のエラーは変更されずにスローされます。さらに、例外でエラーを発生させると、あるタイプのエラーを別のタイプに変換することもできます:

コードをコピー コードは次のとおりです:

試してみてください:
10/0
ZeroDivisionError を除く:
Raise ValueError('入力エラー!')

合理的な変換ロジックがある限り問題ありませんが、IOError を無関係な ValueError に変換してはなりません。

概要

Python の組み込みの try...excit...finally は、エラーを処理するのに非常に便利です。エラーが発生した場合、エラー メッセージを分析し、エラーが発生したコードの場所を特定することが最も重要です。

プログラムは積極的にエラーをスローし、呼び出し元に対応するエラーを処理させることもできます。ただし、どのエラーがスローされるか、およびそのエラーが発生する理由をドキュメントに明確に記載する必要があります。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート