web-dev-qa-db-ja.com

Python hasattr vs getattr

私は最近、hasattrについていくつかの ツイートPythonドキュメント を読んでいます。

hasattr(オブジェクト、名前)

引数はオブジェクトと文字列です。文字列がオブジェクトの属性の1つの名前である場合はTrueになり、そうでない場合はFalseになります。 (これは、getattr(object、name)を呼び出して、AttributeErrorが発生するかどうかを確認することで実装されます。)

Pythonには、私が通常同意する許可よりも許しを求める方が簡単であるというモットーがあります。

この場合、私は本当に単純なpythonコード:

import timeit
definition="""\
class A(object):
    a = 1
a = A()
"""

stm="""\
hasattr(a, 'a')
"""
print timeit.timeit(stmt=stm, setup=definition, number=10000000)

stm="""\
getattr(a, 'a')
"""
print timeit.timeit(stmt=stm, setup=definition, number=10000000)

結果:

$ python test.py
hasattr(a, 'a')
1.26515984535

getattr(a, 'a')
1.32518696785

属性が存在せず、getattrとhasattrの差が大きい場合にどうなるかについても試しました。つまり、これまで見てきたことは、getattrはhasattrよりも遅いということですが、ドキュメントではgetattrを呼び出すと書かれています。

hasattrgetattr のCPython実装を検索しましたが、どちらも次の関数を呼び出しているようです。

v = PyObject_GetAttr(v, name);

しかし、getattrにはhasattrよりも多くの定型文があり、おそらく遅くなります。

ドキュメントでhasattrがgetattrを呼び出すと言っている理由を誰かが知っていますか?実際にはパフォーマンスが原因ではないのに、hasattrの代わりにgetattrを使用するようにユーザーに勧めているようです。それがよりパイソン的であるという理由だけですか?

たぶん私は私のテストで何か間違ったことをしています:)

ありがとう、

ラウル

11
raulcumplido

ドキュメントは奨励していません、ドキュメントはただ明白を述べています。 hasattrはそのように実装されており、プロパティゲッターからAttributeErrorをスローすると、属性が存在しないように見せることができます。これは重要な詳細であり、それがドキュメントに明示的に記載されている理由です。たとえば、次のコードについて考えてみます。

_class Spam(object):
    sausages = False

    @property
    def eggs(self):
        if self.sausages:
            return 42
        raise AttributeError("No eggs without sausages")

    @property
    def invalid(self):
        return self.foobar


spam = Spam()
print(hasattr(Spam, 'eggs'))

print(hasattr(spam, 'eggs'))

spam.sausages = True
print(hasattr(spam, 'eggs'))

print(hasattr(spam, 'invalid'))
_

結果は

_True
False
True
False
_

つまり、Spamクラスにはeggsのプロパティ記述子がありますが、ゲッターは_not self.sausages_の場合にAttributeErrorを発生させるため、そのクラスのインスタンスは「hasattr "eggs

それ以外は、hasattrは次の場合にのみ使用してください値は必要ありません;値が必要な場合は、2つの引数を指定してgetattrを使用し、例外または3つの引数をキャッチします。3番目は適切なデフォルト値です。

getattr()(2.7.9)を使用した結果:

_>>> spam = Spam()
>>> print(getattr(Spam, 'eggs'))
<property object at 0x01E2A570>
>>> print(getattr(spam, 'eggs'))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 7, in eggs
AttributeError: No eggs without sausages
>>> spam.sausages = True
>>> print(getattr(spam, 'eggs'))
42
>>> print(getattr(spam, 'invalid'))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 10, in invalid
AttributeError: 'Spam' object has no attribute 'invalid'
>>>
_
16
Antti Haapala

hasattrは例外を飲み込むことに問題があるようです(少なくとも in Python 2.7 ))、おそらく修正されるまでそれから離れた方が良いでしょう。

たとえば、 次のコード

>>> class Foo(object):
...     @property
...     def my_attr(self):
...         raise ValueError('nope, nope, nope')
...
>>> bar = Foo()
>>> bar.my_attr
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in my_attr
ValueError: nope, nope, nope
>>> hasattr(Foo, 'my_attr')
True
>>> hasattr(bar, 'my_attr')
False
>>> getattr(bar, 'my_attr', None)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in my_attr
ValueError: nope, nope, nope
>>>
2
hvelarde