1. ホーム
  2. パイソン

[解決済み】なぜ "except: pass "は悪いプログラミング手法なのか?

2022-03-24 16:48:02

質問

他のStack Overflowの質問で、よくコメントを見かけます。 except: pass は推奨されません。なぜいけないのでしょうか?私は時々、エラーが何であるかは気にせず、ただコードを続行したいのです。

try:
    something
except:
    pass

なぜ except: pass ブロックが悪いのか?何が悪いのでしょうか?それは、私が pass をエラーにしたり except 何かエラーがありますか?

解決方法は?

正しく推測されるように、それには2つの側面があります。キャッチする 任意の の後に例外型を指定しないことで、エラー except で、何もせずにそのまま渡す。

私の説明は「ちょっと」長いので、Tl;drで説明すると、以下のようになります。

  1. をキャッチしないでください。 任意の エラー . 常に、どの例外から回復する用意があるかを指定し、それらの例外のみをキャッチするようにします。
  2. except ブロックはなるべく使わない . 明示的に望まれていない限り、これは通常、良い兆候ではありません。

でも、詳しく説明しましょう。

をキャッチしないでください。 任意の エラー

を使用する場合 try ブロックは、通常、例外がスローされる可能性があることを知っているからこそ行うのです。そのため、あなたはまた、すでにおおよその が壊れる可能性があり、どのような例外が投げられるか。そのような場合、例外をキャッチするのは 積極的に回復する ということです。つまり、例外に備え、例外が発生した場合に従うべき代替案を持っているのです。

例えば、ユーザーに数値の入力を求める場合、入力された数値を変換するために int() を発生させるかもしれません。 ValueError . これは、ユーザーにもう一度試してもらうことで簡単に回復できます。 ValueError を実行し、再度ユーザーにプロンプトを表示するのが適切なプランでしょう。別の例として、あるファイルから設定を読み込もうとしたときに、そのファイルがたまたま存在しなかったとします。設定ファイルであるため、フォールバックとしてデフォルトの設定がある可能性があります。そのため FileNotFoundError で、単純にデフォルトの設定を適用するのが、ここでは良い計画でしょう。さて、この2つのケースでは、非常に具体的な例外が想定され、そこから回復するための計画も同様に具体的なものとなっています。そのため、それぞれのケースで、明示的に except その特定の は例外となります。

しかし、仮に すべて その場合、リカバリーできる例外の他に、予想外の例外が発生する可能性があります。

先ほどの設定ファイルの例で考えてみましょう。ファイルが見つからない場合、デフォルトの設定を適用し、後で設定を自動的に保存することにするかもしれません(そうすれば、次回からはファイルが存在することになります)。ここで IsADirectoryError または PermissionError の代わりに このような場合、おそらくそのまま続行することはありません。デフォルトの設定を適用することはできますが、後でファイルを保存することができなくなります。また、ユーザーは独自の設定を望んでいる可能性が高いので、デフォルト値を使用することは望ましくないでしょう。そこで、そのことをすぐにユーザーに伝え、おそらくプログラムの実行も中断させたい。しかし、これは小さなコード部分の奥深くで行うことではありません。これはアプリケーションレベルの重要なことなので、トップで処理されるべきなのです。

また、別の簡単な例として Python 2 のイディオム のドキュメントを参照してください。ここでは、コードに単純なタイプミスが存在し、それが原因でコードが壊れています。なぜなら、私たちは あらゆる 例外が発生すると NameError s SyntaxError s . どちらもプログラミング中に誰にでも起こるミスで、コードを出荷するときには絶対に入れたくないミスです。しかし、それらもキャッチしてしまったために、そこで発生したことすらわからなくなり、正しくデバッグするための手助けを失ってしまうのです。

しかし、もっと危険な例外もあり、それに対する備えはほとんどない。例えば、以下のようなものです。 システムエラー これは、何かもっと複雑なことが起こっていて、現在のタスクが続けられなくなる可能性があることを意味します。

いずれにせよ、コードの小規模な部分ですべてに備えるということはまずありえないので、そこは本当に備えている例外だけをキャッチすればいいのです。人によっては、少なくとも Exception のようなものは含まれないので SystemExitKeyboardInterrupt どの 意図的に はアプリケーションを終了させるものですが、これではまだあまりに不特定多数であるというのが私の意見です。私が個人的にキャッチボールを容認しているのは、1箇所だけです。 Exception または単に 任意の この例外ハンドラは、準備されていない例外をログに記録することを唯一の目的としています。そうすることで、予期せぬ例外に関する情報をできるだけ多く保持することができます。この情報を使って、明示的に例外を処理するようにコードを拡張したり(回復できる場合)、バグの場合には、二度と発生しないことを確認するためのテストケースを作成したりできます。しかし、もちろん、それは想定していた例外だけを捕捉していた場合にのみ有効で、想定外の例外は自然に湧き上がってくるのです。

except ブロックはなるべく使わない

特定の例外の一部を明示的に捕捉する場合、何もしない方が良い場合が多々あります。そのような場合、単に except SomeSpecificException: pass がちょうどいい。しかし、ほとんどの場合、(前述のように)回復処理に関連するコードが必要になる可能性があるため、そうではありません。例えば、アクションを再試行したり、代わりにデフォルト値を設定するようなものです。

しかし、そうでない場合、例えば、成功するまで繰り返すようなコードになっている場合は、パスだけで十分です。先ほどの例で言えば、ユーザーに数字を入力してもらうとします。ユーザーは私たちが求めたことを実行しないのが好きだということを知っているので、最初の段階でループに入れるだけにして、次のようにします。

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

例外が発生しなくなるまで試行錯誤を続けるので、exceptブロックの中で特別なことをする必要はありませんから、これでよいのです。しかし、もちろん、なぜ入力を繰り返さなければならないのか、少なくともユーザーに何らかのエラーメッセージを示したいという意見もあるでしょう。

しかし、他の多くの場合、単に except は、キャッチする例外の準備が整っていないことを示すものです。例外が単純なものでない限り ( ValueError または TypeError )、パスできる理由は明白なので、パスだけを避けるようにしましょう。もし本当に何もすることがないのであれば(そしてあなたがそれについて絶対に確信しているなら)、なぜそうなのかというコメントを追加することを検討してください。そうでなければ、exceptブロックを拡張して実際にいくつかの回復コードを含むようにしてください。

except: pass

しかし、最も悪いのは、その両方を組み合わせたものです。これはつまり、私たちが進んでキャッチしている 任意の エラーに対しての準備が全くできていないにもかかわらず そして また、それに対して何もしない。あなたは 少なくとも エラーを記録し、アプリケーションを終了させるために再レイズする可能性もあります (MemoryError の後に通常通り続けられる可能性は低いです)。しかし、ただ渡すだけでは、アプリケーションをある程度維持するだけでなく(もちろんどこでキャッチするかによりますが)、すべての情報を捨ててしまい、エラーを発見することが不可能になります。


つまり、結論は それ以外の例外は、修正すべきミスか、いずれにせよ準備不足である可能性が高い。パッシング 特定 の例外は、本当に何もする必要がないのであれば、問題ありません。それ以外の場合は、ただの思い込みと怠慢の表れです。そして、それは絶対に直した方がいい。