web-dev-qa-db-ja.com

なぜ "except:pass"は悪いプログラミング習慣なのですか?

私はexcept: passの使用がどのように推奨されていないかについての他のStack Overflowの質問に対するコメントをよく見ます。なぜこれは悪いのですか?時々私はただエラーが何であるかを気にしないで、私はただコードを続けたいです。

try:
    something
except:
    pass

except: passブロックを使用するのはなぜ悪いのでしょうか。何が悪いの?それは私がエラーでpassするのか、それとも私が何らかのエラーでexceptするのですか?

283
Vader

あなたが正しく推測したように、それには2つの側面があります:exceptの後に例外タイプを指定せず、そして何もしないで単にそれを渡すことによってanyerrorをキャッチ.

私の説明は「もう少し」長くなっています - だからtl; drこれは次のようになります

  1. anyerror をキャッチしないでください。どの例外から回復する準備ができていて、それらだけをキャッチするかを常に指定してください。
  2. ブロック以外を渡さないようにしてください。明示的に望まれていない限り、これは通常良い兆候ではありません。

しかし、詳細に入りましょう。

anyerrorをキャッチしないでください

tryブロックを使用するときは、例外がスローされる可能性があることがわかっているので、通常これを行います。そのため、whatが壊れる可能性があり、どの例外がスローされる可能性があるかというおおよそのアイデアも既にあります。そのような場合、から確実にを回復できるので、例外をキャッチします。それはあなたが例外のために準備ができていて、あなたがその例外の場合に従うであろういくつかの代わりとなる計画を持っていることを意味します。

たとえば、ユーザーに数値の入力を求める場合、int()を使用して入力を変換できます。これにより、 ValueError が発生する可能性があります。ユーザーに再試行するように依頼するだけで簡単に回復できます。したがって、ValueErrorを見つけて再度ユーザーに確認することが適切な計画となります。別の例としては、ファイルから設定を読みたいが、そのファイルが存在しないことがあります。これは設定ファイルなので、フォールバックとしてデフォルト設定があるかもしれません。そのため、ファイルは必ずしも必要ではありません。したがって、 FileNotFoundError をキャッチして単純にデフォルト設定を適用することが、ここでは良い計画となります。どちらの場合も、私たちは予想していた非常に具体的な例外があり、そこから回復するための同様に具体的な計画を立てています。そのため、いずれの場合も、明示的にexceptという特定の例外だけを使用します。

しかし、everythingをキャッチすると、回復する準備ができている例外に加えて、予期しない例外が発生する可能性もあります。私たちは確かに回復することはできません。または回復しないでください。

上記の設定ファイルの例を見てみましょう。ファイルが見つからない場合は、デフォルトの設定を適用しただけで、後で設定を自動的に保存することにしました(次回はファイルが存在するため)。今度は IsADirectoryError 、または PermissionError のようになります。そのような場合、おそらく継続したくないでしょう。それでもデフォルトの設定を適用できますが、後でファイルを保存できなくなります。また、ユーザーがカスタム構成を使用することを意図している可能性が高いため、デフォルト値を使用することは望ましくありません。それで私達はそれについて直ちにそれをユーザーに伝えたい、そしておそらくプログラムの実行も中止したいでしょう。しかし、それは私たちが小さなコード部分の奥深くでやりたいことではありません。これはアプリケーションレベルで重要なことなので、一番上で処理する必要があります。そのため、例外が発生します。

Python 2の慣用句 には、もう1つの簡単な例が記載されています。ここでは、コードに単純なタイプミスが存在しているため、コードが壊れています。every例外をキャッチしているので、 NameErrorsSyntaxErrors もキャッチします。どちらもプログラミング中に起こる間違いです。また、どちらもコードを出荷するときに含めたくない間違いです。しかし、それらを捕まえたので、それらがそこで発生したということさえ知らず、それを正しくデバッグするための助けを失いません。

しかし、私たちが準備することはまずないより危険な例外もあります。例えば SystemError は通常めったに起こらず、私たちが実際には計画できないものです。それはもっと複雑なことが進行中であることを意味します。それはおそらく私たちが現在の仕事を続けることを妨げるものです。

いずれにせよ、あなたがコードの小規模な部分ですべてのことに備えていることはほとんどありません。ですから、本当にあなたが用意している例外だけを捉えるべきです。 ExceptionSystemExitのような設計によるはアプリケーションを終了させるものではないため、少なくとも KeyboardInterrupt をキャッチすることをお勧めする人もいますが、これはまだ遠い不特定すぎる。私が個人的にExceptionまたは単にanyexceptionをキャッチすることを受け入れている場所は1つだけです。それは準備されていない例外をログに記録するという単一の目的を持つ単一のグローバルアプリケーションレベル例外ハンドラです。にとって。そうすれば、予期しない例外に関する情報を保持することができます。それを使用してコードを明示的に処理したり(例外から回復できる場合)、または(バグの場合は)確実にテストケースを作成したりできます。二度と起こらないでしょう。しかし、もちろん、それは私たちがすでに期待していた例外を捉えたことがある場合にのみうまくいくので、私たちが期待していなかったものは自然にバブルアップするでしょう。

ブロック以外を渡さないようにしてください

少数の特定の例外を明示的にキャッチするときには、何もしなくても問題ないことが多くあります。そのような場合、except SomeSpecificException: passを持つだけで大丈夫です。ただし、ほとんどの場合、これは事実ではありません(リカバリプロセスに関連するコードが必要になる可能性があるためです(前述))。これは、たとえば、アクションを再試行すること、または代わりにデフォルト値を設定することなどです。

そうでない場合は、たとえば、コードがすでに成功するまで繰り返されるように構成されているなどの場合は、単にパスするだけで十分です。上の例から、ユーザーに番号の入力を依頼することをお勧めします。ユーザーは自分たちが求めていることをしたくないということを私たちは知っているので、まず最初にそれをループに入れるかもしれません。

def askForNumber ():
    while True:
        try:
            return int(input('Please enter a number: '))
        except ValueError:
            pass

例外がスローされなくなるまで試行し続けるので、exceptブロックで特別なことをする必要はないので、これで問題ありません。しかしもちろん、私たちは少なくともユーザーに何らかのエラーメッセージを表示して、なぜ彼が入力を繰り返さなければならないのかを伝えることを望んでいると主張するかもしれません。

ただし、他の多くの場合では、単にexceptを渡すことは、キャッチしている例外に対して実際には準備できていないことを示しています。これらの例外が単純で(ValueErrorTypeErrorのように)、そして我々が渡すことができる理由が明白でない限り、単に渡すことを避けるようにしてください。何もすることが本当にない(そしてあなたがそれについて絶対に確信している)のであれば、それではその理由でコメントを追加することを検討してください。それ以外の場合は、exceptブロックを展開して、実際にリカバリコードを含めます。

except: pass

最悪の犯罪者はしかし両方の組み合わせです。これは、anyエラーを気にかけていることを意味しますが、それについては絶対に準備ができていませんおよび私たちはそれについても何もしません。あなたは少なくともエラーを記録したいと思うかもしれませんし、それでもアプリケーションを終了させるためにそれをレイズするかもしれません(MemoryErrorの後であなたが普通のように続けることはできそうにないです)。ただ通過しただけでは(もちろんあなたが捕まえた場所によっては)アプリケーションが多少生きているだけでなく、すべての情報を捨ててエラーを発見することは不可能になります - あなたがそれを発見したのでなければ特にそうです。


つまり、結論は次のとおりです。本当に期待していて、そこから回復する準備ができている例外のみをキャッチします。他のすべてはおそらくあなたが修正すべき間違いか、とにかくあなたがとにかく準備ができていない何かのどちらかです。specificexceptionsを渡しても、実際にそれらについて何かする必要がないのであれば問題ありません。他のすべての場合では、それは単に推定と怠惰のサインです。そして、あなたは間違いなくそれを修正したいのです。

309
poke

ここでの主な問題は、すべてのエラーを無視することです。メモリ不足、CPUの燃焼、ユーザーの停止、プログラムの終了、Jabberwockyがユーザーを殺しています。

これはやり過ぎです。あなたの頭の中では、「このネットワークエラーを無視したい」と考えています。予期せぬ何かがうまくいかない場合、あなたのコードは暗黙のうちに続行し、誰もデバッグできないような完全に予測不可能な方法で中断します。

そのため、エラーの一部だけを無視し、それ以外の部分は無視してください。

257
Aaron Digulla

疑似コードを文字通りに実行してもエラーにはなりません。

try:
    something
except:
    pass

NameErrorを投げる代わりに、それが完全に有効なコードであるかのように。これがあなたの望むものではないことを願っています。

71
YS-L

なぜ“ except:pass”は悪いプログラミング習慣なのでしょうか?

なぜこれは悪いのですか?

try:
    something
except:
    pass

これはGeneratorExitKeyboardInterrupt、およびSystemExitを含むあらゆる可能性のある例外をキャッチします。これらは、おそらくキャッチするつもりはない例外です。 BaseExceptionをキャッチするのと同じです。

try:
    something
except BaseException:
    pass

古い バージョンの ドキュメントでは、 と述べています。

Pythonのすべてのエラーが例外を発生させるので、except:を使用すると、多くのプログラミングエラーがランタイム問題のように見える可能性があり、それがデバッグプロセスを妨げます。

Python例外階層

親の例外クラスをキャッチすると、それらのすべての子クラスもキャッチします。あなたが処理する準備ができている例外だけをキャッチするほうがはるかにエレガントです。

これがPython 3 例外の階層構造 です - 本当にすべてを捉えたいですか?:

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- StopAsyncIteration
      +-- ArithmeticError
      |    +-- FloatingPointError
      |    +-- OverflowError
      |    +-- ZeroDivisionError
      +-- AssertionError
      +-- AttributeError
      +-- BufferError
      +-- EOFError
      +-- ImportError
           +-- ModuleNotFoundError
      +-- LookupError
      |    +-- IndexError
      |    +-- KeyError
      +-- MemoryError
      +-- NameError
      |    +-- UnboundLocalError
      +-- OSError
      |    +-- BlockingIOError
      |    +-- ChildProcessError
      |    +-- ConnectionError
      |    |    +-- BrokenPipeError
      |    |    +-- ConnectionAbortedError
      |    |    +-- ConnectionRefusedError
      |    |    +-- ConnectionResetError
      |    +-- FileExistsError
      |    +-- FileNotFoundError
      |    +-- InterruptedError
      |    +-- IsADirectoryError
      |    +-- NotADirectoryError
      |    +-- PermissionError
      |    +-- ProcessLookupError
      |    +-- TimeoutError
      +-- ReferenceError
      +-- RuntimeError
      |    +-- NotImplementedError
      |    +-- RecursionError
      +-- SyntaxError
      |    +-- IndentationError
      |         +-- TabError
      +-- SystemError
      +-- TypeError
      +-- ValueError
      |    +-- UnicodeError
      |         +-- UnicodeDecodeError
      |         +-- UnicodeEncodeError
      |         +-- UnicodeTranslateError
      +-- Warning
           +-- DeprecationWarning
           +-- PendingDeprecationWarning
           +-- RuntimeWarning
           +-- SyntaxWarning
           +-- UserWarning
           +-- FutureWarning
           +-- ImportWarning
           +-- UnicodeWarning
           +-- BytesWarning
           +-- ResourceWarning

しないでください

この形式の例外処理を使用している場合

try:
    something
except: # don't just do a bare except!
    pass

そうするとCtrl-Cでsomethingブロックを中断することはできません。あなたのプログラムはtryコードブロック内のすべての可能な例外を見逃します。

これは、同じ望ましくない動作をする別の例です。

except BaseException as e: # don't do this either - same as bare!
    logging.info(e)

代わりに、探していることがわかっている特定の例外だけをキャッチするようにしてください。たとえば、変換時に値エラーが発生する可能性があることがわかっている場合は、次のようにします。

try:
    foo = operation_that_includes_int(foo)
except ValueError as e:
    if fatal_condition(): # You can raise the exception if it's bad,
        logging.info(e)   # but if it's fatal every time,
        raise             # you probably should just not catch it.
    else:                 # Only catch exceptions you are prepared to handle.
        foo = 0           # Here we simply assign foo to 0 and continue. 

別の例でさらに説明する

WebスクレイピングをしていてUnicodeErrorと言っているのでそれをしているかもしれませんが、他の根本的な欠陥があるかもしれない最も広いExceptionキャッチを使用しているので、あなたのコードは完了まで実行しようとします帯域幅、処理時間、機器の消耗、メモリ不足、ゴミデータの収集など.

他の人々があなたのコードに頼ることができるようにあなたに完了するように頼んでいるならば、私はただすべてを処理することを強いられていると感じることを理解します。しかし、あなたが開発中に騒々しく失敗しても構わないと思っているのであれば、断続的にしか現れないかもしれない問題を修正する機会がありますが、それは長期にわたる費用のかかるバグになります。

より正確なエラー処理を使用すると、コードはより堅牢になります。

46
Aaron Hall
>>> import this

Tim PetersによるPythonの禅

美しさは醜いよりも優れています。
明示的は暗黙的より優れています。
単純なものは複雑なものより優れています。
複雑は複雑よりも優れています。
フラットは入れ子よりも優れています。
疎は密よりも優れています。
読みやすさが重要です。
特別な場合は、ルールを破るほど十分に特別なわけではありません。
実用性は純度よりも優れていますが。
エラーが黙って渡されることは決してありません。
明示的に沈黙していない限り。
あいまいさに直面して、推測することを誘惑を拒否します。
それをするには一つの、そして好ましくは一つだけの明白な方法があるべきです。
あなたがオランダ人でない限り、そのやり方は最初は明白ではないかもしれませんが。
今は決してないよりはましです。
今は決して正しいよりも優れていることはありません。
実装の説明が難しい場合、それは悪い考えです。
実装が説明しやすいのであれば、それは良い考えかもしれません。
名前空間は素晴らしいアイデアの1つです - それらをもっとやろう!

だから、これが私の意見です。エラーを見つけたら、それを処理するために何かをするべきです。すなわち、それをログファイルか何か他のものに書きます。少なくとも、以前はエラーがあったことを知らせます。

27
Booster

SystemExitKeyboardInterruptなどのシステム例外をキャッチしないようにするには、少なくともexcept Exception:を使用する必要があります。これが ドキュメントへのリンク です。

一般に、不要な例外をキャッチしないように、キャッチしたい例外を明示的に定義する必要があります。あなたはあなたがどんな例外を無視するのか知っておくべきです

23
Tupteq

まず、Pythonの Zenの2つの原則に違反しています

  • 明示的は暗黙的より優れています
  • エラーは黙って渡されるべきではありません

それが意味することは、あなたがあなたの意図的にあなたのエラーを静かに通過させるということです。さらに、except: passは例外をキャッチするため、どのエラーが正確に発生したのかは、イベントではわかりません。

次に、私たちがPythonのZenから抜け出して正気の観点から言えば、except:passを使うことで知識も制御もなしになることを知っておくべきです。システム。経験則は、エラーが発生した場合は例外を発生させ、適切な処置をとることです。あなたが前もって知らないならば、これらがどのような行動であるべきであるか、少なくともどこかにエラーを記録してください(そして、もっと良い例外を発生させる):

try:
    something
except:
    logger.exception('Something happened')

しかし、通常、例外をキャッチしようとすると、おそらく何か間違ったことをしていることになります!

12

#1の理由はすでに述べられています - それはあなたが期待していなかったエラーを隠します。

(#2) - 他の人がコードを読み、理解するのを難しくします。ファイルを読み込もうとしているときにFileNotFoundExceptionをキャッチすると、他の開発者にとっては、 'catch'ブロックがどのような機能を持つべきかは明らかです。例外を指定しない場合は、ブロックが何をすべきかを説明するために追加のコメントが必要です。

(#3) - これは遅延プログラミングを示しています。一般的なtry/catchを使用する場合は、実行可能時間がわからないことを示しています。プログラムにエラーがあるか、Pythonでどんな例外が発生する可能性があるかわかりません。特定のエラーを見つけることは、あなたが自分のプログラムとPythonが投げるエラーの範囲の両方を理解していることを示しています。他の開発者やコードレビュー担当者があなたの仕事を信頼するようになる可能性が高くなります。

12
Kevin

では、このコードはどのような出力を生成しますか?

fruits = [ 'Apple', 'pear', 'carrot', 'banana' ]

found = False
try:
     for i in range(len(fruit)):
         if fruits[i] == 'Apple':
             found = true
except:
     pass

if found:
    print "Found an Apple"
else:
    print "No apples in list"

try-exceptブロックが複雑なオブジェクト階層への何百行もの呼び出しであり、それ自体が大きなプログラムの呼び出しツリーの途中で呼び出されると想像してください。プログラムがうまくいかなくなったとき、どこを見始めますか?

11
Ian Harvey

except:pass構文は、try:ブロックでカバーされているコードが実行されている間に発生するあらゆる例外条件を本質的に沈黙させます。

この悪い習慣をしているのは、通常それがあなたが本当に望んでいるものではないということです。もっと頻繁に、あなたが沈黙したいという特定の条件が出てきます、そしてexcept:passはあまりにも鈍い楽器です。それは仕事を成し遂げるでしょうが、それはまたあなたが予想していなかったであろう他のエラー状態を隠しますが、非常にうまく他の方法で対処したいかもしれません。

これをPythonで特に重要にしているのは、この言語の慣用句では、例外が必ずしもエラーではないことです。もちろん、ほとんどの言語と同じように、このように使用されることがよくあります。しかし、特にPythonは時々それらを使用して、通常の実行中のケースの一部ではないいくつかのコードタスクからの代替出口パスを実装していますが、それでも時折登場することが知られており、ほとんどの場合でも予想されます。 SystemExitはすでに古い例として言及されていますが、今日最も一般的な例はStopIterationです。このように例外を使用することは、特にイテレータとジェネレータが最初にPythonに導入されたときには、多くの論争を引き起こしましたが、結局その考えが普及しました。

11
The Spooniest

一般に、エラー/例外は、 3つのカテゴリ のいずれかに分類できます。

  • 致命的:あなたのせいではなく、あなたはそれらを防ぐことができず、あなたはそれらから回復することができません。あなたは確かにそれらを無視して続行し、あなたのプログラムを未知の状態にしておくべきではありません。エラーによってプログラムが強制終了されるだけで、対処できることは何もありません。

  • 骨抜き:あなた自身の責任です。おそらく見落とし、バグ、あるいはプログラミングの誤りが原因です。あなたはバグを直すべきです。繰り返しますが、あなたは最も確実に無視し続けるべきではありません。

  • 外因性ファイルが見つからないまたは接続が終了したなどの例外的な状況でこれらのエラーが発生する可能性があります。 )あなたは明示的にこれらのエラーを扱うべきです、そしてこれらだけ。

すべての場合において、except: passはあなたのプログラムを未知の状態にするだけで、より多くの損害を与える可能性があります。

私の意見では、エラーが現れる理由があります、それは私の音がバカだということですが、それはそうです。良いプログラミングはあなたがそれらを処理しなければならないときにだけエラーを発生させます。また、少し前に読んだように、「pass-statementはコードを表示するStatementであり、後で挿入される」ので、空のexcept-statementを使用したい場合は気軽に挿入できますが、良いプログラムにはそうなります。不足している部分になります。あなたが持っているべきものを扱ってはいけないからです。例外が表示されると、入力データを修正したりデータ構造を変更したりすることができます。そのため、これらの例外は再度発生しません(ほとんどの場合(ネットワーク例外、一般入力例外)例外はプログラムの次の部分がうまく実行されません)。たとえば、NetworkExceptionは、ネットワーク接続が切断されていることを示し、プログラムは次のプログラムステップでデータを送受信できません。

ただし、1つのexecption-blockだけにpassブロックを使用することは有効です。なぜなら、例外の種類を区別するためです。すべてのexception-blocksを1つにまとめても、空ではありません。

try:
    #code here
except Error1:
    #exception handle1

except Error2:
    #exception handle2
#and so on

そのように書き直すことができます:

try:
    #code here
except BaseException as e:
    if isinstance(e, Error1):
        #exception handle1

    Elif isinstance(e, Error2):
        #exception handle2

    ...

    else:
        raise

そのため、passステートメントを含む複数のexcept-blocksでさえ、その構造が特別なタイプの例外を処理するコードになる可能性があります。

5
Sirac

簡単に言えば、例外やエラーがスローされた場合、何かがおかしいということです。それはそれほど悪いことではないかもしれませんが、gotoステートメントを使用するためだけにエラーや例外を作成、スロー、およびキャッチすることはお勧めできません。それはめったに行われません。 99%の時間で、どこかに問題がありました。

問題に対処する必要があります。それが人生のどのように、プログラミングにおいてもそうであるように、あなたがただ問題を放置してそれらを無視しようとするならば、彼らはただ彼ら自身で何度もなくなるのではありません。代わりに、彼らは大きくなって増殖します。問題が発生してさらに先へ進むことを防ぐには、1)問題を解決して後で混乱を解消するか、2)問題を封じ込めてその後で混乱を解消する必要があります。

例外やエラーを無視してそのようにしておくことは、メモリリーク、未解決のデータベース接続、ファイルのアクセス許可に対する不必要なロックなどを経験するのに良い方法です。

ごくまれに、問題が非常に小さく、ささいなこと、そして - try ... catchブロックを必要とすることを除けば - 自己完結型なので、実際にはあとでクリーンアップするのは面倒なことではありません。このベストプラクティスが必ずしも当てはまらないのはこれらの場合だけです。私の経験では、これは一般的に、コードが実行していることは基本的にささいで忘れがちなことであり、再試行や特別なメッセージのようなものは複雑さもスレッドを妨げる価値もありません。

私の会社では、規則はキャッチブロック内でほぼ常に何かを実行することです。もしあなたが何もしなければ、あなたは常に非常に正当な理由でコメントを書かなければなりません。何故なの。やるべきことがあるときは、絶対に空のcatchブロックを通過または放置してはいけません。

5
Panzercrisis

これまでに挙げたすべてのコメントは有効です。可能な場合は、無視したい例外を正確に指定する必要があります。可能であれば、例外の原因を分析し、無視しようとしていることだけを無視し、それ以外は無視する必要があります。例外がアプリケーションを「壮観に」クラッシュさせるのであれば、それは、問題が発生したことを隠すよりも、予期せぬ事態が起こったときに起こったことを知ることがはるかに重要だからです。

すべてのことを言っても、最重要事項としてプログラミングの練習をしないでください。これはばかです。 ignore-all-exceptionsブロックを実行する時間と場所は常にあります。

慣用的な最優先事項のもう1つの例は、goto演算子の使用法です。私が学校に通っていたとき、私たちの教授は私たちにgoto演算子を教えただけでした。 xyzは絶対に使用すべきではないと言っている人がいることを信じてはいけません。また、役に立つ場合にはシナリオはあり得ません。いつもあります。

4
galets

エラー処理はプログラミングにおいて非常に重要です。何が悪いのかユーザーに見せる必要があります。ごくまれに、エラーを無視することができます。これは非常に悪いプログラミング方法です。

2
fastcodejava

まだ言及されていないので、 contextlib.suppress を使用することをお勧めします。

with suppress(FileNotFoundError):
    os.remove('somefile.tmp')

上記の例では、例外が発生してもしなくても、プログラムの状態は変わりません。つまり、somefile.tmpは常に存在しなくなります。

0
Mateen Ulhaq