Pythonでは、「許可を求める」(タイプ/条件のチェック)よりも「許しを請う」(例外をキャッチする)ほうがよいとよく耳にします。 Pythonでのアヒル入力の強制に関して、これは
try:
x = foo.bar
except AttributeError:
pass
else:
do(x)
より良いか悪いか
if hasattr(foo, "bar"):
do(foo.bar)
else:
pass
パフォーマンス、読みやすさ、「Pythonic」、またはその他の重要な要素の点で?
例外がスローされると思う頻度に本当に依存します。
私の意見では、どちらのアプローチも、少なくとも可読性とPython性の観点からは同等に有効です。ただし、オブジェクトの90%が属性bar
を持たない場合、2つのアプローチの間に明確なパフォーマンスの違いに気付くでしょう:
>>> import timeit
>>> def askforgiveness(foo=object()):
... try:
... x = foo.bar
... except AttributeError:
... pass
...
>>> def askpermission(foo=object()):
... if hasattr(foo, 'bar'):
... x = foo.bar
...
>>> timeit.timeit('testfunc()', 'from __main__ import askforgiveness as testfunc')
2.9459929466247559
>>> timeit.timeit('testfunc()', 'from __main__ import askpermission as testfunc')
1.0396890640258789
しかし、オブジェクトの90%doが属性を持っている場合、テーブルは変換されています。
>>> class Foo(object):
... bar = None
...
>>> foo = Foo()
>>> timeit.timeit('testfunc(foo)', 'from __main__ import askforgiveness as testfunc, foo')
0.31336188316345215
>>> timeit.timeit('testfunc(foo)', 'from __main__ import askpermission as testfunc, foo')
0.4864199161529541
したがって、パフォーマンスの観点からは、状況に最適なアプローチを選択する必要があります。
結局のところ、 timeit
module を戦略的に使用することは、あなたができる最もPython的なことかもしれません。
pythonでは、Pythonの方法でパフォーマンスを向上させることがよくあります。他の言語では、フロー制御に例外を使用することは、一般にひどい考えとみなされます。通常、異常なオーバーヘッドが発生しますが、この手法はPythonで明示的に承認されているため、インタープリターはこのタイプのコード用に最適化されています。
すべてのパフォーマンスの質問と同様に、確実に行う唯一の方法は、コードをプロファイルすることです。両方のバージョンを記述し、どちらがより速く動作するかを確認します。私の経験では、「Pythonの方法」が通常、最も速い方法です。
パフォーマンスは副次的な問題だと感じています。それが発生した場合、プロファイラーは実際のボトルネックに焦点を合わせるのに役立ちます。これは、可能性のある違法な引数を処理する方法である場合とそうでない場合があります。
一方、読みやすさとシンプルさは常に最大の関心事です。ここには難しいルールはありません。あなたの判断で行ってください。
これは普遍的な問題ですが、環境固有または言語固有の規則が関係します。たとえば、Pythonでは、通常、期待する属性を使用し、考えられるAttributeErrorを呼び出し元に到達させるだけで問題ありません。
正確さに関しては、例外処理が適切であると思います(ただし、私はhasattr()アプローチを自分で使用することもあります)。 hasattr()に依存することの基本的な問題は、コードコントラクトの違反をサイレントエラーに変えることです(これは、存在しないプロパティをスローしないJavaScriptの大きな問題です)。