1. ホーム
  2. python

属性の存在を確認するのに最適な方法はどれですか?[重複しています]。

2023-09-07 16:55:19

質問

属性の存在を確認する方法として、どちらが良いでしょうか?

Jarret Hardie はこのように回答しています。

if hasattr(a, 'property'):
    a.property

このような方法もあるのですね。

if 'property' in a.__dict__:
    a.property

あるアプローチは通常、他のアプローチよりも多く使われているのでしょうか?

どのように解決するのですか?

ベストな方法はありません。 なぜなら、ある属性が存在するかどうかを確認するためだけでは決してなく、それは常に何らかの大きなプログラムの一部だからです。 いくつかの正しい方法と、1 つの顕著な間違った方法があります。

間違った方法

if 'property' in a.__dict__:
    a.property

このテクニックが失敗することを示すデモを紹介します。

class A(object):
    @property
    def prop(self):
        return 3

a = A()
print "'prop' in a.__dict__ =", 'prop' in a.__dict__
print "hasattr(a, 'prop') =", hasattr(a, 'prop')
print "a.prop =", a.prop

出力します。

a.__dict__の'prop' = False
hasattr(a, 'prop') = True
a.prop = 3

たいていの場合、あなたは __dict__ . これは特別なことをするための特別な属性であり、属性が存在するかどうかを確認することはかなり平凡なことです。

EAFPの方法

Pythonでよく使われる慣用句に "easy to ask for forgiveness than permission" 略してEAFP があります。 属性の存在をチェックするためだけでなく、このイディオムを使用する多くのPythonコードを目にすることでしょう。

# Cached attribute
try:
    big_object = self.big_object
    # or getattr(self, 'big_object')
except AttributeError:
    # Creating the Big Object takes five days
    # and three hundred pounds of over-ripe melons.
    big_object = CreateBigObject()
    self.big_object = big_object
big_object.do_something()

これは、存在しないかもしれないファイルを開くのと全く同じイディオムであることに注意してください。

try:
    f = open('some_file', 'r')
except IOError as ex:
    if ex.errno != errno.ENOENT:
        raise
    # it doesn't exist
else:
    # it does and it's open

また、文字列を整数に変換するための

try:
    i = int(s)
except ValueError:
    print "Not an integer! Please try again."
    sys.exit(1)

オプションモジュールのインポートも...

try:
    import readline
except ImportError:
    pass

LBYL流

この hasattr という方法も、もちろん有効です。 このテクニックは、quot;look before you leap"、略してLBYLと呼ばれています。

# Cached attribute
if not hasattr(self, 'big_object'):
    big_object = CreateBigObject()
    self.big_object = CreateBigObject()
big_object.do_something()

(この hasattr は Python 3.2 より前のバージョンでは例外に関して奇妙な振る舞いをします -- 捕まえるべきでない例外を捕らえてしまいます -- が、そのような例外はまずないので、これはおそらく無関係でしょう。 しかし、そのような例外が発生する可能性は低いので、これはおそらく無関係でしょう。 hasattr のテクニックは try/except の方が遅いのですが、気にするほど頻繁に呼び出すわけではありませんし、その差はそれほど大きくありません。 最後に hasattr はアトミックではないので AttributeError を投げるかもしれません。しかし、これはあまり考えにくいシナリオで、スレッドの周りではとにかく慎重になる必要があります。 これら 3 つの違いのいずれも、心配するほどのものだとは思いません)。

使用方法 hasattr よりもはるかに簡単です。 try/except よりも単純で、属性が存在するかどうかを知る必要があるだけです。 私にとっての大きな問題は、PythonプログラマーとしてEAFP手法を読むのに慣れているため、LBYL手法が"奇妙"に見えるということです。 もし上記の例を書き直して LBYL スタイルを使うように書き直すと、不器用か、全く正しくないか、あるいは書くのが難しすぎるコードになってしまいます。

# Seems rather fragile...
if re.match('^(:?0|-?[1-9][0-9]*)$', s):
    i = int(s)
else:
    print "Not an integer! Please try again."
    sys.exit(1)

そして、LBYLは時々、完全に不正確である。

if os.path.isfile('some_file'):
    # At this point, some other program could
    # delete some_file...
    f = open('some_file', 'r')

オプションモジュールをインポートするためのLBYL関数を書きたいのなら、どうぞご自由に...この関数は完全にモンスターになりそうですね。

getattrの方法

デフォルト値が必要なだけなら getattr の短いバージョンです。 try/except .

x = getattr(self, 'x', default_value)

デフォルト値を構成するのが高価な場合、次のようなものになります。

x = getattr(self, 'attr', None)
if x is None:
    x = CreateDefaultValue()
    self.attr = x

また、もし None が可能な値です。

sentinel = object()

x = getattr(self, 'attr', sentinel)
if x is sentinel:
    x = CreateDefaultValue()
    self.attr = x

まとめ

内部的には getattrhasattr ビルトインは、単に try/except のテクニックを使うだけです (C 言語で書かれたものを除く)。 つまり、重要なところではすべて同じように動作し、正しいものを選ぶのは状況やスタイルの問題によるのです。

try/except EAFP のコードは常に一部のプログラマを不愉快にさせるでしょうし、また hasattr/getattr LBYLコードは他のプログラマーを苛立たせるでしょう。 どちらも正しいし、どちらかを選ばなければならない本当に説得力のある理由もないことが多いのです。 (しかし、他のプログラマは、属性が未定義であることが正常であると考えることにうんざりしていますし、一部のプログラマは Python で未定義の属性を持つことが可能であることに恐怖を感じています)。