JavaでのLBYL対EAFP?
-
03-07-2019 - |
質問
私は最近自分でPythonを教えていて、コード実行前のエラーチェックに関するLBYL / EAFPイディオムを発見しました。 Pythonでは、受け入れられているスタイルはEAFPであり、言語でうまく機能しているようです。
LBYL( L B の前に Y ou L eap ):
def safe_divide_1(x, y):
if y == 0:
print "Divide-by-0 attempt detected"
return None
else:
return x/y
EAFP( E A sk F の方が、 P 放出よりも容易です>):
def safe_divide_2(x, y):
try:
return x/y
except ZeroDivisionError:
print "Divide-by-0 attempt detected"
return None
これは、JavaとC ++のバックグラウンドに由来するEAFPを主要なデータ検証コンストラクトとして使用したことを聞いたことがありません。 EAFPはJavaで使用するのが賢明ですか?または、例外によるオーバーヘッドが大きすぎますか?例外が実際にスローされたときにのみオーバーヘッドがあることを知っているので、EAFPのより単純なメソッドが使用されない理由はわかりません。好みですか?
解決
個人的には、これは慣習によって裏付けられていると思いますが、EAFPは決して良い方法ではありません。 次のようなものとして見ることができます:
if (o != null)
o.doSomething();
else
// handle
ではなく:
try {
o.doSomething()
}
catch (NullPointerException npe) {
// handle
}
さらに、次のことを考慮してください:
if (a != null)
if (b != null)
if (c != null)
a.getB().getC().doSomething();
else
// handle c null
else
// handle b null
else
// handle a null
これはそれほどエレガントではないように見えるかもしれません(そして、これは大雑把な例です-耐えてください) NullPointerException
をクリックし、それを取得した場所と理由を把握してください。
まれな状況を除き、EAFPの使用方法は決して使用しないでください。また、はい、問題が発生したため、例外がスローされない場合でもtry-catchブロックにオーバーヘッドが発生します。
他のヒント
ファイルにアクセスしている場合、EABYはLBYLよりも信頼性が高くなります。これは、LBYLに関連する操作がアトミックではなく、ファイルシステムが見ているときと跳躍しているときに変化するためです。実際には、標準名はTOCTOU-チェック時間、使用時間です。不正確なチェックによって引き起こされるバグは、TOCTOUのバグです。
一意の名前が必要な一時ファイルの作成を検討してください。選択したファイル名がまだ存在するかどうかを確認する最良の方法は、ファイルを作成してみることです。ファイルが既に存在する場合は、オプションを使用して操作が失敗することを確認してください(POSIX / Unixの用語では、へのO_EXCLフラグopen()
)。 (おそらく access()
を使用して)ファイルが既に存在するかどうかをテストしようとすると、「いいえ」と表示されるまでの間ファイルを作成しようとしたときに、誰かまたは他の誰かがファイルを作成した可能性があります。
逆に、既存のファイルを読み取ろうとしているとします。ファイルが存在することを確認(LBYL)すると「そこにあります」と表示される場合がありますが、実際に開くと「存在しない」ことがわかります。
どちらの場合も、最終的な動作を確認する必要があります-LBYLは自動的に助けにはなりませんでした。
(SUIDまたはSGIDプログラムをいじっている場合、 access()
は別の質問をします。それはLBYLに関連する可能性がありますが、コードは失敗の可能性を考慮する必要があります。 )
PythonとJavaの例外の相対的なコストに加えて、それらの間には哲学/態度に違いがあることに留意してください。 Javaは、型(およびその他すべて)について非常に厳密にしようとするため、クラス/メソッドシグネチャの明示的で詳細な宣言が必要です。どんな時点でも、使用しているオブジェクトの種類とそれができることを正確に知る必要があると想定しています。対照的に、Pythonの「ダックタイピング」は、つまり、オブジェクトのマニフェストタイプが何であるかが確実にわからない(そして気にする必要はない)ことを意味します。この種の寛容な環境では、唯一の正気な態度は、物事はうまくいくと推測することですが、うまくいかない場合は結果に対処する準備ができています。 Javaの自然な制限は、このようなカジュアルなアプローチにはうまく合いません。 (これは、アプローチや言語を非難することを意図したものではなく、これらの態度は各言語のイディオムの一部であると言うことを意図しており、異なる言語間でイディオムをコピーすると、しばしば不器用さとコミュニケーション不足につながる可能性があります...)
例外はJavaよりもPythonでより効率的に処理されます。そのため、少なくともその構成がPythonで見られるのは部分的にです。 Javaでは、そのように例外を使用する方が(パフォーマンスの点で)非効率的です。
これらのコードスニペットを検討してください:
def int_or_default(x, default=0):
if x.isdigit():
return int(x)
else:
return default
def int_or_default(x, default=0):
try:
return int(x)
except ValueError:
return default
どちらも正しいように見えますか?しかし、そのうちの1つはそうではありません。
LBYLを使用する前者は、 isdigit
と isdecimal
の微妙な違いのために失敗します。文字列"①²³🄅₅"で呼び出されると、デフォルト値を正しく返すのではなくエラーをスローします。
EAFTPを使用すると、定義により、正しい処理が行われます。要件を必要とするコードはその要件をアサートするコードであるため、振る舞いの不一致の範囲はありません。
LBYLを使用するとは、内部ロジックを取得して、それらを every 呼び出しサイトにコピーすることを意味します。要件の標準的なエンコーディングを1つ持つのではなく、関数を呼び出すたびに混乱する可能性があります。
EAFTPは例外について ではないことに注意してください。特にJavaコードは例外を広く使用するべきではありません。適切なコードブロックに適切な仕事を与えることです。例として、 Optional
戻り値を使用することは、EAFTPコードを記述するための完全に有効な方法であり、正確性を確保するためにLBYLよりもはるかに効果的です。