web-dev-qa-db-ja.com

LBYL対JavaのEAFP?

私は最近自分自身を教えていたPythonで、コード実行前のエラーチェックに関してLBYL/EAFPのイディオムを発見しました。Pythonでは、受け入れられているスタイルはEAFPであり、言語。

LBYL([〜#〜] l [〜#〜]ook- [〜#〜] 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 [〜#〜] ermission) :

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の単純なメソッドが使用されない理由がわかりません。

58
ryeguy

個人的には、これは慣例に裏付けられていると思いますが、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

これはエレガントさに欠けるかもしれませんが(もちろん、これは大まかな例です-我慢してください)、エラーを処理する際に、すべてをtry-catchでラップしてNullPointerException、次に、どこで、なぜそれが得られたのかを理解してみてください。

EAFPの見方は、まれな状況を除いて、決して使用すべきではありません。また、問題が発生したため、例外がスローされない場合でもはい、try-catchブロックではオーバーヘッドが発生します

9
Yuval Adam

ファイルにアクセスしている場合、EABYはLBYLよりも信頼性が高くなります。これは、LBYLに関連する操作がアトミックではなく、ファイルシステムが、見ている時間と跳躍する時間の間で変わる可能性があるためです。実際、標準名はTOCTOU-チェックの時間、使用の時間です。不正確なチェックによって引き起こされるバグはTOCTOUバグです。

一意の名前が必要な一時ファイルを作成することを検討してください。選択したファイル名がまだ存在するかどうかを確認する最善の方法は、ファイルを作成してみることです。ファイルがすでに存在する場合は、オプションを使用して操作が失敗することを確認してください(POSIX/Unixの用語では、open())。ファイルが既に存在するかどうかをテストしようとした場合(おそらくaccess()を使用)、「いいえ」と表示されてからファイルを作成しようとするまでの間に、誰かまたは他の誰かがファイル。

逆に、既存のファイルを読み取ろうとしたとします。ファイルが存在することを確認すると(LBYL)、「そこにある」と表示される場合がありますが、実際にファイルを開くと、「存在しない」ことがわかります。

どちらの場合も、最終的な操作を確認する必要があります-LBYLは自動的には役に立ちませんでした。

(SUIDまたはSGIDプログラムをいじっている場合、access()は別の質問をします。これはLBYLに関連している可能性がありますが、コードは失敗の可能性を考慮に入れる必要があります。)

120

PythonとJavaの例外の相対的なコストに加えて、哲学/態度には違いがあることに注意してください。Javaは、型(およびその他すべて)について厳密であり、クラス/メソッドシグネチャの明示的で詳細な宣言が必要です。使用するオブジェクトの型とオブジェクトが実行できる機能をいつでも正確に把握している必要があります。対照的に、Pythonの「ダックタイピング」とは、オブジェクトのマニフェストタイプが何であるか(また、気にする必要もない)が確実にわからないことを意味します。環境、唯一の正気な態度は、物事がうまくいくと仮定することですが、うまくいかない場合の結果に対処する準備をすることです。Javaの自然な制限は、このようなカジュアルなアプローチにうまく適合しません。アプローチまたは言語ではなく、これらの態度は各言語のイディオムの一部であり、イディオムのコピーetweenが異なる言語を使用すると、多くの場合、ぎこちなくなり、コミュニケーションが不十分になります...)

46
Jeff Shannon

例外は、Javaの場合よりもPythonのほうが効率的に処理されます。少なくとも部分的に Pythonでその構造を見る理由です。Javaでは、(パフォーマンス)例外をそのように使用します。

11
mipadi

次のコードスニペットを検討してください。

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を使用しており、isdigitisdecimalの微妙な違いにより失敗します。文字列「①²³????₅」で呼び出されると、デフォルト値を正しく返すのではなく、エラーをスローします。

後で、EAFTPを使用すると、定義により、正しく処理されます。要件を必要とするコードis要件を表明するコードであるため、動作の不一致の範囲はありません。

LBYLを使用するということは、内部ロジックを取り、それらをevery call-siteにコピーすることを意味します。要件を1つの正規のエンコードにするのではなく、関数を呼び出すたびに失敗する可能性があります。

EAFTP is n't例外について、そしてJavaコードは例外を広範囲に使用すべきではないことに注意してください。適切なジョブを適切なブロックに与えることです。例として、Optionalの戻り値を使用することは、EAFTPコードを書くのに完全に有効な方法であり、正確さを保証するためにLBYLよりもはるかに効果的です。

5
Veedrac