if hasattr(obj, 'attribute'):
# do somthing
vs
try:
# access obj.attribute
except AttributeError, e:
# deal with AttributeError
どちらを優先する必要があり、その理由は何ですか?
回答:
hasattr
内部的にはtry/except
ブロックと同じタスクを迅速に実行します。これは非常に具体的で最適化された1つのタスクのツールであるため、該当する場合は、非常に汎用的な代替ツールよりも優先されます。
try
操作が機能するはずであることを伝えることができます。けれどもtry
さんの意図は、より読みやすいと考えられるかもしれないので、それは、一般的であり、必ずしもそのようではありません。
パフォーマンスの違いを説明するベンチはありますか?
それはあなたの友達です
$ python -mtimeit -s 'class C(object): a = 4
c = C()' 'hasattr(c, "nonexistent")'
1000000 loops, best of 3: 1.87 usec per loop
$ python -mtimeit -s 'class C(object): a = 4
c = C()' 'hasattr(c, "a")'
1000000 loops, best of 3: 0.446 usec per loop
$ python -mtimeit -s 'class C(object): a = 4
c = C()' 'try:
c.a
except:
pass'
1000000 loops, best of 3: 0.247 usec per loop
$ python -mtimeit -s 'class C(object): a = 4
c = C()' 'try:
c.nonexistent
except:
pass'
100000 loops, best of 3: 3.13 usec per loop
$
|positive|negative
hasattr| 0.446 | 1.87
try | 0.247 | 3.13
try
はの約2倍の速度hasattr()
です。そうでない場合は、try
より約1.5倍遅くなりますhasattr()
(両方とも、属性が存在する場合よりも大幅に遅くなります)。これはおそらく、ハッピーパスではtry
ほとんど何もしないためです(Pythonは、例外を使用するかどうかに関係なく、例外のオーバーヘッドをすでに支払っています)hasattr()
が、名前の検索と関数呼び出しが必要です。不幸な道のりでは、どちらも例外処理とを実行する必要がありますgoto
がhasattr()
、PythonバイトコードではなくCで実行します。
3番目の、そして多くの場合より良い代替案があります。
attr = getattr(obj, 'attribute', None)
if attr is not None:
print attr
利点:
getattr
Martin Geiserによって指摘された悪い例外を飲み込む動作はありません-古いPythonでは、hasattr
を飲み込むことさえありKeyboardInterrupt
ます。
オブジェクトに属性があるかどうかを確認する通常の理由は、属性を使用できるようにするためであり、これは当然その属性につながります。
属性はアトミックに読み取られ、他のスレッドがオブジェクトを変更することから安全です。(ただし、これが大きな懸念事項である場合は、オブジェクトにアクセスする前にオブジェクトをロックすることを検討することをお勧めします。)
よりも短くtry/finally
、多くの場合、よりも短くなりhasattr
ます。
広いexcept AttributeError
ブロックはAttributeErrors
、期待しているもの以外のものをキャッチする可能性があり、混乱する動作につながる可能性があります。
属性へのアクセスは、ローカル変数へのアクセスよりも遅くなります(特に、プレーンインスタンス属性でない場合)。(ただし、正直なところ、Pythonでのマイクロ最適化はばか者の用事であることがよくあります。)
注意すべきことの1つobj.attribute
は、Noneに設定されている場合を気にする場合は、別の番兵値を使用する必要があるということです。
私はほとんどいつも使用しますhasattr
:それはほとんどの場合正しい選択です。
問題のあるケースは、クラスがオーバーライドする場合です__getattr__
:期待どおりにキャッチするのではなく、すべての例外hasattr
をキャッチしAttributeError
ます。つまり、例外b: False
を確認する方が適切な場合でも、以下のコードが出力されますValueError
。
class X(object):
def __getattr__(self, attr):
if attr == 'a':
return 123
if attr == 'b':
raise ValueError('important error from your database')
raise AttributeError
x = X()
print 'a:', hasattr(x, 'a')
print 'b:', hasattr(x, 'b')
print 'c:', hasattr(x, 'c')
したがって、重要なエラーはなくなりました。これはPython3.2(issue9666)で修正され、現在はをキャッチするだけです。hasattr
AttributeError
簡単な回避策は、次のようなユーティリティ関数を作成することです。
_notset = object()
def safehasattr(thing, attr):
return getattr(thing, attr, _notset) is not _notset
これでgetattr
状況に対処して、適切な例外を発生させることができます。
safehasattr
使用getattr
する場合は、を使用して値をローカル変数にコピーします。これはほとんどの場合です。
hasattr
ように改善されたとは知りませんでした。
hasattr
、そしてチェックに行きました。hasattrが^ Cを飲み込んだところにいくつかの面白いbzrバグがありました。
設計上、関数が属性のないオブジェクトを受け入れるかどうかによって異なります。たとえば、関数の呼び出し元が2つあり、1つは属性をオブジェクトに提供し、もう1つは属性を持たないオブジェクトを提供する場合など、。
属性のないオブジェクトを取得する唯一のケースが何らかのエラーによるものである場合は、よりクリーンな設計であると信じているため、遅くなる可能性がありますが、例外メカニズムを使用することをお勧めします。
結論:これは効率の問題ではなく、デザインと読みやすさの問題だと思います。
このテーマは、SebastianWitowskiによるEuroPython2016の講演「 WritingfastPython」で取り上げられました。これは彼のスライドの複製とパフォーマンスの要約です。彼はまた、あなたがこの議論に飛び込む前に、そのキーワードにタグを付けるためにここで言及する価値のある用語の外観を使用しています。
属性が実際に欠落している場合、許しを求めることは許可を求めることよりも遅くなります。したがって、経験則として、属性が欠落している可能性が非常に高いことがわかっている場合、または予測可能なその他の問題がある場合は、許可を求める方法を使用できます。そうでなければ、コードがほとんどの場合読み取り可能なコードになると予想する場合
# CASE 1 -- Attribute Exists
class Foo(object):
hello = 'world'
foo = Foo()
if hasatter(foo, 'hello'):
foo.hello
## 149ns ##
try:
foo.hello
except AttributeError:
pass
## 43.1 ns ##
## 3.5 times faster
# CASE 2 -- Attribute Absent
class Bar(object):
pass
bar = Bar()
if hasattr(bar, 'hello'):
bar.hello
## 428 ns ##
try:
bar.hello
except AttributeError :
pass
## 536 ns ##
## 25% slower
実用的な観点から、ほとんどの言語では、条件付きを使用すると、例外を処理するよりも常にかなり高速になります。
現在の関数の外部に存在しない属性のケースを処理したい場合は、例外がより良い方法です。条件の代わりに例外を使用したいという指標は、条件が単にフラグを設定して現在の操作を中止し、他の場所でこのフラグをチェックしてそれに基づいてアクションを実行することです。
とはいえ、Rax Olgudが指摘するように、他の人とのコミュニケーションはコードの重要な属性の1つであり、「これは私が期待することです」ではなく「これは例外的な状況です」と言いたいことがより重要かもしれません。 。
最初。
短いほど良いです。例外は例外的である必要があります。
for
ステートメントの最後に例外があり、それもhasattr
使用します。ただし、「短い方が良い」(および「単純な方が良い」!)が適用されるため、より単純で、より短く、より具体的なhasattrが実際に望ましいです。