Pythonの許しと許可とダックタイピング


44

Pythonでは、「許可を求める」(タイプ/条件チェック)よりも「許しを請う」(例外をキャッチする)の方が良いとよく耳にします。Pythonでのダックタイピングの強制に関して、これは

try:
    x = foo.bar
except AttributeError:
    pass
else:
    do(x)

より良いか悪い

if hasattr(foo, "bar"):
    do(foo.bar)
else:
    pass

パフォーマンス、可読性、「pythonic」、またはその他の重要な要素の面で?


17
3番目のオプションがあります。何もせず、バーなしのfooをバグとして扱います
jk。

hasattrその正確なtry / catchで内部的に実装されていると聞いたことを覚えています。それが本当かどうかは定かではありません...(プロパティで異なる動作をしますよね?多分私はgetattr..を考えているでしょう。)
Izkata

@Izkata:の実装はhasattrgetattrTrue成功した場合は成功し、失敗した場合は)C-APIに相当するものを使用しますが、CでのFalse例外処理ははるかに高速です。
マーティンピーターズ

2
私はmartijnの答えを受け入れましたが、属性を設定しようとしている場合は、セッターなしのプロパティである可能性があるため、try / catchの使用を必ず検討する必要があります。その場合、hasattrはtrueです、それでもAttributeErrorが発生します。
-darkfeline

回答:


60

例外がスローされると考える頻度に大きく依存します。

私の意見では、両方のアプローチは、少なくとも読みやすさとpythonic-nessの点では等しく有効です。ただし、オブジェクトの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%はならない属性を持って、テーブルを回転されています:

>>> 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モジュールの戦略的な使用は、あなたができる最もPython的なものかもしれません。


1
数週間前、私は次の質問をしました:Programmers.stackexchange.com/questions/161798/… そこで、緩やかに型付けされた言語では、型チェックを行うために余分に作業する必要があるかと尋ねました。私があなたが持っているのを知っています。
Tulainsコルドバ

@ user1598390:型の同種混合を期待するAPIを定義する場合、いくつかのテストを行う必要があります。ほとんどの場合、そうではありません。これは、全体としてパラダイムに関するルールを導き出すことができない特定の領域です、私は恐れています。
マーティンピーターズ

まあ、深刻なシステム開発には、APIの定義が含まれます。コンパイラーはコンパイル時に型をチェックするので、コードを少なくする必要があるため、型厳密言語がそのために最適だと思います。
Tulainsコルドバ

1
@GarethRees:テスト対象の関数に引数を渡す、答えの後半のパターンを確立します。
マーティンピーターズ

1
なお、ためhasattr、オブジェクトは、Python内の属性がアクセスしようとすることです持っているかどうかを判断する唯一の一般的な方法が判明するので、それは実際には、とにかく試し-除くフードの下のC-APIに相当しません。
user2357112 14

11

Pythonでは、多くの場合、Pythonの方法でパフォーマンスを向上させます。他の言語では、フロー制御に例外を使用することは一般にひどい考えと見なされます。なぜなら、例外は通常、並外れたオーバーヘッドを課すからです。しかし、この手法はPythonで明示的に承認されているため、インタープリターはこのタイプのコードに最適化されています。

パフォーマンスに関するすべての質問と同様に、確実にする唯一の方法は、コードをプロファイルすることです。両方のバージョンを作成し、どちらがより高速に動作するかを確認します。私の経験では、「Pythonの方法」は通常最速の方法です。


3

パフォーマンスは二次的な関心事だと思います。発生した場合、プロファイラーは実際のボトルネックに焦点を当てるのに役立ちます。これは、考えられる違法な引数の処理方法である場合とそうでない場合があります。

一方、読みやすさとシンプルさは常に最大の関心事です。ここには厳格なルールはありません、あなたの判断を使用してください。

これは普遍的な問題ですが、環境または言語固有の規則が関連しています。たとえば、Pythonでは通常、期待する属性を単純に使用して、可能性のあるAttributeErrorを呼び出し元に到達させることができます。


-1

正確さの観点から、私は例外処理が進むべき道だと思います(ただし、私は時々自分でhasattr()アプローチを使用します)。hasattr()に依存することの基本的な問題は、コードコントラクトの違反をサイレントエラーに変えることです(これは、JavaScriptの大きな問題であり、存在しないプロパティをスローしません)。


3
あなたの答えは、他の人によってすでに述べられていることをはるかに超えるようには見えません。他のサイトとは異なり、プログラマは答えの背後にある理由を説明する答えを期待しています。無言の失敗の問題で良い点に触れますが、それに正義を与える必要はありません。以下の5月のヘルプを読む:programmers.stackexchange.com/help/how-to-answer

私が最後に行った回答は、広すぎると批判されました。短く簡潔にしようと思った。
ジョー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.