web-dev-qa-db-ja.com

存在しない属性を処理するhasattr()とtry-exceptブロック

if hasattr(obj, 'attribute'):
    # do somthing

try:
    # access obj.attribute
except AttributeError, e:
    # deal with AttributeError

どちらを優先すべきか、またその理由は何ですか?

77
Imran

hasattrは内部でtry/exceptブロックと同じタスクを迅速に実行します。これは非常に具体的で最適化されたワンタスクツールです。

77
Alex Martelli

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

timeitそれはあなたの友達です

$ 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
82
ZeD

3番目の、そしてしばしばより良い代替手段があります。

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

利点:

  1. getattrには悪いものはありません Martin Geiserが指摘した例外嚥下動作 -古いPythonでは、hasattrKeyboardInterruptを飲み込みます。

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

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

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

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

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

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

20
poolie

私はほとんど常にhasattrを使用します。ほとんどの場合、これは正しい選択です。

問題のあるケースは、クラスが__getattr__hasattrは、期待どおりAttributeErrorだけをキャッチするのではなく、すべての例外をキャッチします。つまり、以下のコードはb: FalseValueError例外を表示する方が適切であっても:

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')

したがって、重要なエラーはなくなりました。これは Python 3.2)で修正issue9666 )で、hasattrAttributeErrorのみをキャッチします。

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

_notset = object()

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

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

17
Martin Geisler

私はそれがあなたの関数が属性なしでオブジェクトを受け入れるかどうかに依存すると言うでしょう設計による、例えば関数の2つの呼び出し元がある場合、1つはオブジェクトに属性を提供し、もう1つはオブジェクトを属性なしで提供します。

属性なしでオブジェクトを取得する唯一のケースが何らかのエラーが原因である場合、よりクリーンなデザインであると考えられるため、例外メカニズムを使用することをお勧めします。

結論:効率の問題ではなく、設計と可読性の問題だと思います。

13
Roee Adler

属性がない場合はnotエラー状態であるため、例外処理バリアントには問題があります:来る可能性があるAttributeErrorsもキャッチしますinternallyobj.attributeにアクセスする場合(たとえば、属性はプロパティであるため、アクセスするとコードが呼び出されるため)。

4
UncleZeiv

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

4
n8gray

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

また、pythonには イディオム があり、EAFP(許可よりも赦しを求める方が簡単)]はLBYL( 'leap before you leap')よりも優れています。

3
Douglas Leeder

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

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

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

2
Curt J. Sampson

このテーマは、EuroPython 2016の講演高速Pythonの記述でカバーされていました。ここに、パフォーマンスの概要を含む彼のスライドの複製があります。彼はまた、この議論で飛躍する前に、用語lookを使用しています。そのキーワードにタグを付けるためにここで言及する価値があります。

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

3権限OR許し?

# 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
2
jxramos

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

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

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

そして

try: obj.attr
except: ...

hasattrは最初のケースを使用して結果を決定するだけです。思考の糧 ;-)

0
Levit

最初。

短いほど良い。例外は例外的でなければなりません。

0
Unknown