86

私はこの哲学について個人的な「宗教的な」意見を求めているのではなく、もう少し技術的な意見を求めています。

このフレーズは、コードが「pythonic」であるかどうかを確認するためのいくつかのリトマス試験の1つであることを理解しています。しかし、私にとって、pythonicは、クリーンでシンプルかつ直感的であり、悪いコーディングのための例外ハンドラーがロードされていないことを意味します。

だから、実際の例。クラスを定義します:

class foo(object):
    bar = None

    def __init__(self):
        # a million lines of code
        self.bar = "Spike is my favorite vampire."
        # a million more lines of code

さて、手続き型のバックグラウンドから来て、別の関数でこれを実行したいと思います:

if foo.bar:
    # do stuff

せっかちで、最初のfoo = Noneを実行しなかった場合、属性例外が発生します。それで、「許可ではなく許しを求める」は、代わ​​りにこれを行うべきだと示唆していますか?

try:
    if foo.bar:
        # do stuff
except:
    # this runs because my other code was sloppy?

クラス定義をよりあいまいなままにするために、tryブロックにロジックを追加する方がよいのはなぜですか?最初にすべてを定義して、明示的に許可を与えてはどうでしょうか。

(try /exceptブロックの使用について私を殴らないでください...私はどこでもそれらを使用します。私は完全なプログラマーではなかったので、自分のエラーをキャッチするためにそれらを使用するのは正しくないと思います。)

それとも...私は「許しを求める」というマントラを完全に誤解していますか?

4

8 に答える 8

82

「許可ではなく許しを求める」は、2つのプログラミングスタイルに反対します。「許可を求める」は次のようになります。

if can_do_operation():
    perform_operation()
else:
    handle_error_case()

「許しを求める」は次のようになります。

try:
    perform_operation()
except Unable_to_perform:
    handle_error_case()

これは、操作の実行に失敗することが予想される状況であり、何らかの方法で操作が不可能な状況に対処する必要があります。たとえば、操作がファイルにアクセスしている場合、そのファイルは存在しない可能性があります。

許しを求める方がよい主な理由は2つあります。

  • 同時実行の世界(マルチスレッドプログラムの場合、または操作にファイル、他のプロセス、ネットワークリソースなどのプログラムの外部にあるオブジェクトが含まれる場合)では、実行時と実行時の間で状況が変わる可能性がありますcan_do_operation()。実行しますperform_operation()。したがって、とにかくエラーを処理する必要があります。
  • 許可を求めるには、正確に正しい基準を使用する必要があります。間違えると、実行できる操作ができなくなったり、やっぱり操作できないためにエラーが発生したりします。たとえば、ファイルを開く前にファイルが存在するかどうかをテストする場合、ファイルは存在する可能性がありますが、権限がないため開くことができません。逆に、ファイルを開いたときにファイルが作成される場合があります(たとえば、ファイルが存在するかどうかを確認するだけではなく、実際にファイルを開いたときにのみ起動されるネットワーク接続を介してファイルが作成されるため)。

許しを求める状況に共通しているのは、操作を実行しようとしていて、操作が失敗する可能性があることを知っているということです。

を書くときfoo.bar、の存在しbarないことは通常、オブジェクトの失敗とは見なされませんfoo。これは通常、プログラマーのエラーです。設計されていない方法でオブジェクトを使用しようとしています。Pythonでのプログラマーエラーの結果は、未処理の例外です(運が良ければ、もちろん、一部のプログラマーエラーは自動的に検出できません)。したがって、barがオブジェクトのオプション部分である場合、これを処理する通常の方法は、にbar初期化されたフィールドを持ちNone、オプション部分が存在する場合は他の値に設定することです。存在するかどうかをテストするbarには、

if foo.bar is not None:
     handle_optional_part(foo.bar)
else:
     default_handling()

ブール値として解釈されたときに常にtrueになる場合if foo.bar is not None:if foo.bar:のみ省略できます— 0 、、、または偽の真理値を持つ他のオブジェクトである可能性がある場合は、が必要です。オプションのパーツをテストする場合(との間のテストとは対照的に)、より明確になります。barbar[]{}is not NoneTrueFalse

この時点で、次のように尋ねることができます。bar存在しない場合の初期化を省略し、ハンドラーでその存在をテストするかhasattr、ハンドラーでキャッチしAttributeErrorませんか?コードが意味をなすのは2つの場合だけだからです。

  • オブジェクトにはbarフィールドがありません。
  • オブジェクトには、barあなたが考えていることを意味するフィールドがあります。

barしたがって、オブジェクトを作成または使用することを決定するときは、オブジェクトに別の意味のフィールドがないことを確認する必要があります。フィールドのない別のオブジェクトを使用する必要がある場合bar、適応する必要があるのはおそらくそれだけではないため、派生クラスを作成するか、オブジェクトを別のクラスにカプセル化することをお勧めします。

于 2012-09-04T19:49:28.693 に答える
80

古典的な「許可ではなく許しを求める」例は、dict存在しない可能性のあるからの値にアクセスすることです。例えば:

names = { 'joe': 'Joe Nathan', 'jo': 'Jo Mama', 'joy': 'Joy Full' }
name = 'hikaru'

try:
    print names[name]
except KeyError:
    print "Sorry, don't know this '{}' person".format(name)

ここでは、発生する可能性のある例外(KeyError)が示されているため、発生する可能性のあるすべてのエラーについて許しを求めるのではなく、自然に発生するエラーのみを求めます。比較のために、「最初に許可を求める」アプローチは次のようになります。

if name in names:
    print names[name]
else:
    print "Sorry, don't know this '{}' person".format(name)

また

real_name = names.get(name, None)
if real_name:
    print real_name
else:
    print "Sorry, don't know this '{}' person".format(name)

このような「許しを求める」の例は、単純すぎることがよくあります。tryIMO /ブロックが本質的に/exceptよりも優れていることは明確ではありません。解析など、さまざまな方法で失敗する可能性のある操作を実行する場合、実際の値ははるかに明確になります。使用; オペレーティングシステム、ミドルウェア、データベース、またはネットワークリソースへのアクセス。または複雑な数学を実行します。複数の潜在的な障害モードがある場合、許しを得るために準備することは非常に価値があります。ifelseeval()

コード例に関するその他の注意事項:

tryすべての変数の使用法の周りにひしゃく/exceptブロックする必要はありません。それは恐ろしいでしょう。また、上記の定義で設定されているため、を設定self.barする必要はありません。通常、クラス(クラスのすべてのインスタンス間で共有される可能性が高いデータの場合)または(インスタンスデータの場合、各インスタンスに固有の)のいずれかで定義します。__init__()class__init__()

ちなみに、の値Noneは未定義でもエラーでもありません。これは特定の正当な値であり、none、nil、null、またはnothingを意味します。0多くの言語にはそのような値があるため、プログラマーは、、、(空の文字列)または同様の有用な値 -1を「オーバーロード」しません。''

于 2012-09-04T14:40:16.803 に答える
19

ここには良い答えがたくさんありますが、今まで見たことのない点を付け加えたいと思いました。

多くの場合、許可の代わりに許しを求めると、パフォーマンスが向上します。

  • 許可を求める場合は、毎回追加の操作を行って許可を求める必要があります。
  • 許しを求めるとき、あなたは時々、すなわちそれが失敗したとき、余分な操作を実行する必要があるだけです。

通常、失敗するケースはまれです。つまり、許可を求めているだけの場合は、追加の操作を行う必要はほとんどありません。はい、失敗すると例外がスローされ、追加の操作が実行されますが、Pythonでの例外は非常に高速です。ここでいくつかのタイミングを見ることができます:https ://jeffknupp.com/blog/2013/02/06/write-cleaner-python-use-exceptions/

于 2017-07-26T03:33:28.963 に答える
8

あなたは正しいです-あなたのずさんなコーディングの目的でtryあり、それをカバーすることではありません。exceptそれはただよりずさんなコーディングにつながります。

例外処理は、例外的な状況を処理するために使用する必要があります(ずさんなコーディングは例外的な状況ではありません)。ただし、多くの場合、どの例外的な状況が発生する可能性があるかを予測するのは簡単です。(たとえば、プログラムはユーザー入力を受け入れ、それを使用して辞書にアクセスしますが、ユーザーの入力は辞書のキーではありませんでした...)

于 2012-09-04T14:21:50.600 に答える
7

私の個人的な非宗教的な意見は、このマントラは主に文書化され、よく理解されている終了条件とエッジケース(I / Oエラーなど)に適用され、ずさんなプログラミングの脱獄カードとして使用してはならないというものです。

とはいえtry/except、「より良い」代替案が存在する場合によく使用されます。例えば:

# Do this    
value = my_dict.get("key", None)

# instead of this
try:
  value = my_dict["key"]
except KeyError:
  value = None

あなたの例として、if hasattr(foo, "bar")あなたが制御できずfoo、あなたの期待との適合性をチェックする必要がある場合は使用してください。そうでない場合は、単に使用foo.barして、結果として生じるエラーを、ずさんなコードを識別して修正するためのガイドにしてください。

于 2012-09-04T14:28:09.450 に答える
6

すでに多くの質の高い回答がありますが、ほとんどの場合、機能的なものに当てはまるように、文体の観点からこれについて説明します。

(マルチスレッドプログラムの外で)正しいコードを保証する許可ではなく、許しを求める必要がある場合があります。

正規の例は、

if file_exists: 
    open_it()

この例では、チェックと実際にファイルを開こうとしている間にファイルが削除されている可能性があります。これは、以下を使用することで回避できtryます。

try:
    open_it()
except FileNotFoundException:
    pass # file doesn't exist 

これはさまざまな場所で発生し、多くの場合、ファイルシステムや外部APIで機能します。

于 2018-01-05T02:19:21.050 に答える
5

Pythonのコンテキストでは、「許可ではなく許しを求める」とは、期待どおりの結果が得られるかどうかをチェックせず、そうでない場合に発生するエラーを処理するプログラミングのスタイルを意味します。古典的な例は、辞書に次のように特定のキーが含まれていることを確認しないことです。

d = {}
k = "k"
if k in d.keys():
  print d[k]
else:
  print "key \"" + k + "\" missing"

ただし、キーが欠落している場合に発生するエラーを処理するには、次のようにします。

d = {}
k = "k"
try:
  print d[k]
except KeyError:
  print "key \"" + k + "\" missing"

ただし、重要なのはif、コード内のすべてをtry/に置き換えることではありませんexcept。それはあなたのコードを明らかに厄介にするでしょう。代わりに、エラーについて実際に何かできる場合にのみエラーをキャッチする必要があります。理想的には、これによりコード内の全体的なエラー処理の量が減り、実際の目的がより明確になります。

于 2012-09-04T14:38:55.087 に答える
2

許可ではなく許しを求めることは、コードを単純化することを意味します。.barこれは、AttributeErrorをトリガーする可能性のある合理的な期待がある場合に、このようにコードを記述することを目的としています。

 try:
     print foo.bar
 except AttributeError as e
     print "No Foo!" 

あなたのコードは許可と許しの両方を求めているようです:)

問題は、何かが失敗することを合理的に予想する場合は、try/catchを使用することです。何かが失敗することを予期せず、とにかく失敗した場合、スローされる例外は、他の言語で失敗したアサーションと同等になります。予期しない例外が発生している場所を確認し、それに応じてコード/仮定を調整します。

于 2012-09-04T14:22:14.883 に答える