hasattr()vstry-存在しない属性を処理するためのブロックを除く


回答:


82

hasattr内部的にはtry/exceptブロックと同じタスクを迅速に実行します。これは非常に具体的で最適化された1つのタスクのツールであるため、該当する場合は、非常に汎用的な代替ツールよりも優先されます。


8
競合状態を処理するためにtry / catchブロックが必要な場合を除きます(スレッドを使用している場合)。
ダグラスリーダー2009年

1
または、私が遭遇した特殊なケース:値のないdjango OneToOneField:hasattr(obj、field_name)はFalseを返しますが、field_nameの属性があります:DoesNotExistエラーが発生するだけです。
Matthew Schinckel 2011

3
Python2.xのすべての例外hasattrキャッチすることに注意してください。例と簡単な回避策については、私の回答を参照しください。
マーティンガイスラー2013

5
興味深いコメントtry操作機能するはずであることを伝えることができます。けれどもtryさんの意図は、より読みやすいと考えられるかもしれないので、それは、一般的であり、必ずしもそのようではありません。
ioannis Filippidis 2014年

88

パフォーマンスの違いを説明するベンチはありますか?

それはあなたの友達です

$ 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

16
興味深い、具体的な数字を提供するための+1。実際、「試行」は、一般的なケースが含まれている場合(つまり、Python例外が本当に例外的である場合)に効率的です。
Eric O Lebigot 2009年

これらの結果をどのように解釈するかわかりません。ここではどちらが速く、どれだけ速いですか?
Stevoisiak

2
@ StevenM.Vascellaro:属性が存在する場合、tryはの約2倍の速度hasattr()です。そうでない場合は、tryより約1.5倍遅くなりますhasattr()(両方とも、属性が存在する場合よりも大幅に遅くなります)。これはおそらく、ハッピーパスではtryほとんど何もしないためです(Pythonは、例外を使用するかどうかに関係なく、例外のオーバーヘッドをすでに支払っています)hasattr()が、名前の検索と関数呼び出しが必要です。不幸な道のりでは、どちらも例外処理とを実行する必要がありますgotohasattr()、PythonバイトコードではなくCで実行します。
ケビン

24

3番目の、そして多くの場合より良い代替案があります。

attr = getattr(obj, 'attribute', None)
if attr is not None:
     print attr

利点:

  1. getattrMartin Geiserによって指摘された悪い例外を飲み込む動作はありません-古いPythonでは、hasattrを飲み込むことさえありKeyboardInterruptます。

  2. オブジェクトに属性があるかどうかを確認する通常の理由は、属性を使用できるようにするためであり、これは当然その属性につながります。

  3. 属性はアトミックに読み取られ、他のスレッドがオブジェクトを変更することから安全です。(ただし、これが大きな懸念事項である場合は、オブジェクトにアクセスする前にオブジェクトをロックすることを検討することをお勧めします。)

  4. よりも短くtry/finally、多くの場合、よりも短くなりhasattrます。

  5. 広いexcept AttributeErrorブロックはAttributeErrors、期待しているもの以外のものをキャッチする可能性があり、混乱する動作につながる可能性があります。

  6. 属性へのアクセスは、ローカル変数へのアクセスよりも遅くなります(特に、プレーンインスタンス属性でない場合)。(ただし、正直なところ、Pythonでのマイクロ最適化はばか者の用事であることがよくあります。)

注意すべきことの1つobj.attributeは、Noneに設定されている場合を気にする場合は、別の番兵値を使用する必要があるということです。


1
+ 1-これはdict.get( 'my_key'、 'default_value')と

1
存在を確認し、デフォルト値で属性を使用する一般的なユースケースに最適です。
dsalaj 2015年

18

私はほとんどいつも使用します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.2issue9666)で修正され、現在はをキャッチするだけです。hasattrAttributeError

簡単な回避策は、次のようなユーティリティ関数を作成することです。

_notset = object()

def safehasattr(thing, attr):
    return getattr(thing, attr, _notset) is not _notset

これでgetattr状況に対処して、適切な例外を発生させることができます。


2
これはまたして少し改善されたようでのpython2.6をhasattr意志少なくともキャッチしていないKeyboardInterruptなど
poolie

または、をsafehasattr使用getattrする場合は、を使用して値をローカル変数にコピーします。これはほとんどの場合です。
poolie 2013年

@poolieいいですね、そのhasattrように改善されたとは知りませんでした。
マーティンガイスラー2013年

はい、それは良いです。誰かに避けるように言った今日まで、私もそれを知りませんでしたhasattr、そしてチェックに行きました。hasattrが^ Cを飲み込んだところにいくつかの面白いbzrバグがありました。
poolie 2013年

2.7から3.6へのアップグレード中に問題に直面していました。この回答は、問題を理解して解決するのに役立ちます。
KameshJungi19年

13

設計上、関数が属性のないオブジェクトを受け入れるかどうかによって異なります。たとえば、関数の呼び出し元が2つあり、1つは属性をオブジェクトに提供し、もう1つは属性を持たないオブジェクトを提供する場合など、。

属性のないオブジェクトを取得する唯一のケースが何らかのエラーによるものである場合は、よりクリーンな設計であると信じているため、遅くなる可能性がありますが、例外メカニズムを使用することをお勧めします。

結論:これは効率の問題ではなく、デザインと読みやすさの問題だと思います。


1
「試してみる」がコードを読む人にとって意味がある理由を主張するための+1。:)
Eric O Lebigot 2009年

5

属性を持たないことがエラー条件でない場合、例外処理バリアントには問題があります。obj.attributeにアクセスするときに内部的に発生する可能性のあるAttributeErrorsもキャッチします(たとえば、属性はプロパティであるため、アクセスするとコードが呼び出されます)。


私の意見では、これはほとんど無視されてきた大きな問題です。
リックは

5

このテーマは、SebastianWitowskiによるEuroPython2016の講演 WritingfastPython」で取り上げられました。これは彼のスライドの複製とパフォーマンスの要約です。彼はまた、あなたがこの議論に飛び込む前に、そのキーワードにタグを付けるためにここで言及する価値のある用語の外観を使用しています

属性が実際に欠落している場合、許しを求めることは許可を求めることよりも遅くなります。したがって、経験則として、属性が欠落している可能性が非常に高いことがわかっている場合、または予測可能なその他の問題がある場合は、許可を求める方法を使用できます。そうでなければ、コードがほとんどの場合読み取り可能なコードになると予想する場合

3つの許可または許し?

# 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

4

テストしている属性が1つだけの場合は、を使用すると思いますhasattr。ただし、存在する場合と存在しない場合がある属性に複数のアクセスを行う場合は、tryブロックを使用すると、入力を節約できる場合があります。


3

オプション2をお勧めします。他のスレッドが属性を追加または削除している場合、オプション1には競合状態があります。

また、Pythonにはイディオムがあり、EAFP(「許可よりも許しを求めるのが簡単」)はLBYL(「飛躍する前に見る」)よりも優れています。


2

実用的な観点から、ほとんどの言語では、条件付きを使用すると、例外を処理するよりも常にかなり高速になります。

現在の関数の外部に存在しない属性のケースを処理したい場合は、例外がより良い方法です。条件の代わりに例外を使用したいという指標は、条件が単にフラグを設定して現在の操作を中止し、他の場所でこのフラグをチェックしてそれに基づいてアクションを実行することです。

とはいえ、Rax Olgudが指摘するように、他の人とのコミュニケーションはコードの重要な属性の1つであり、「これは私が期待することです」ではなく「これは例外的な状況です」と言いたいことがより重要かもしれません。 。


条件付きテストと比較して、「試行」は「これは例外的な状況です」と解釈できるという事実を主張するための+1。:)
Eric O Lebigot 2009年

0

最初。

短いほど良いです。例外は例外的である必要があります。


5
Pythonでは例外が非常に一般的です。すべてのforステートメントの最後に例外があり、それもhasattr使用します。ただし、「短い方が良い」(および「単純な方が良い」!)が適用されるため、より単純で、より短く、より具体的なhasattrが実際に望ましいです。
Alex Martelli

@Alexは、Pythonパーサーがこれらのステートメントを1に変換するからといって、それが非常に一般的であることを意味するわけではありません。彼らがそのシンタックスシュガーを作ったのには理由があります:それであなたはブロックを除いてtryをタイプすることの無愛想さにとらわれません。
不明

例外が例外的な場合は、「明示的な方が良い」、元の投稿者の2番目のオプションの方が良い、と私は言います…
Eric O Lebigot 2009年

0

少なくとも、プログラムで何が起こっているのか、読みやすさなどの人間的な部分を除外している場合(実際には、ほとんどの場合、パフォーマンスよりも不滅です(少なくともこの場合は-そのパフォーマンススパンで)、 Roee Adlerらが指摘したように)。

それにもかかわらず、その観点からそれを見ると、それはどちらかを選択する問題になります

try: getattr(obj, attr)
except: ...

そして

try: obj.attr
except: ...

以来、hasattrちょうど結果を決定するために、第1のケースを使用しています。思考の糧 ;-)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.