2012-03-17 6 views
54

属性の有無を確認するには、どちらの方法が適していますか?属性の存在を確認する最良の方法はどれですか?

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

は、一つのアプローチ一般的に他の人よりも多く使用されている:?私はそれがこの方法をも行うことができることがわかり

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

:この回答を提供

Jarret Hardie

+0

あなたの2番目のオプションは間違っていますが、とにかく質問に答えた[second answer](http://stackoverflow.com/a/610923/1132524)です。 –

答えて

124

属性が存在するかどうかを確認するだけではないので、「最良」の方法はありません。です。それはいつもより大きなプログラムの一部です。いくつかの正しい方法と顕著な方法があります。

間違った方法ここで

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 

は出力:

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

ほとんどの時間、あなたはしたくありません__dict__をつぶす。それは特別なことを行うための特別な属性であり、属性が存在するかどうかを確認することはかなり平凡です。 Pythonで

EAFPの道

共通イディオムは略して「許可よりも許しを求めるしやすい」、または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) 

LBYLの道

hasattr方法は、当然のことながら、あまりにも動作します。この技法は、「あなたが飛躍する前に見る」、または略してLBYLと呼ばれています。

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

hasattr組み込みが実際に例外に関して3.2より前のバージョンのPythonで動作がおかしい - それはいけないという例外をキャッチします - しかし、そのような例外はほとんどありませんので、これは、おそらく無関係です。 hasattrのテクニックもtry/exceptよりも遅いですが、それほど頻繁に呼び出すことはなく、違いはあまり大きくありません。最後にhasattrはアトミックではないので、別のスレッドが属性を削除するとAttributeErrorとにかくスレッドに関しては非常に注意する必要があります。私はこれらの3つの違いのいずれかを心配する価値はないと考えています。)

hasattrを使用すると、属性が存在するかどうかを知る必要がある限り、try/exceptよりはるかに簡単です。私の大きな問題は、LBYLのテクニックが "奇妙"に見えるということです。なぜなら、Pythonのプログラマーとして、私はEAFPテクニックの読み方に慣れているからです。上記の例を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方法

getattrtry/exceptの短いバージョンです。

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 

結論:デフォルト値は構築する高価である場合

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

、あなたはこのようなものになってしまいます

内部的には、getattrおよびhasattrビルトインtry/exceptテクニックを使用してください(C言語で書かれている場合を除く)。だから、彼らはみな同じように振る舞い、正しいものを選ぶのは状況とスタイルの問題によるものです。

try/except EAFPコードは、常に一部のプログラマーに間違ったやり方をぶつけ、hasattr/getattr LBYLコードは他のプログラマーに影響を与えます。彼らはどちらも正しいですし、しばしばどちらかを選ぶ本当の理由がありません。 (しかし、他のプログラマは、あなたはそれが通常の属性が未定義であるために検討することにうんざりしている、といくつかのプログラマが、それはPythonで未定義の属性を持つことも可能だと恐怖です。)

+4

おそらく 'try ... except'は' x'が 'x.attr'という属性を持つことがほとんどいつも期待される方が良いでしょう。 (例外は本当に例外的です。) –

+3

私はほとんどの場合、 'try ... except'を個人的に使うと、' hasattr'メソッドは私の口に悪い味を残します。 –

+0

@Dietrich Epp:上記の 'try/except'のような例外を隠す可能性のあるコードは、' hasattr() 'より明らかに悪いです。 – jfs

10

hasattr()*方法です。

a.__dict__は醜いので、多くの場合は機能しません。 hasattr()は実際に属性を取得しようとしてAttributeErrorを内部的にキャッチしますので、カスタム__getattr__()メソッドを定義しても機能します。属性を要求しないようにするに

は二回getattr()のための第三引数を使用することができる:

not_exist = object() 

# ... 
attr = getattr(obj, 'attr', not_exist) 
if attr is not_exist: 
    do_something_else() 
else: 
    do_something(attr) 

それはあなたのケースでは、より適切である場合は、だけではなく、not_existセンチネルのデフォルト値を使用することができます。

私は好きではないtry: do_something(x.attr) \n except AttributeError: ..それはAttributeErrordo_something()内部の機能を隠すことがあります。

*Before Python 3.1 hasattr() suppressed all exceptions(だけではなくAttributeError)それが望ましくない場合getattr()を使用する必要があります。

4

hasattr()は、これを行うにはPythonの方法です。それを学んで、それを愛してください。

if varName in locals() or in globals(): 
    do_something() 
else: 
    do_something_else() 

は私が個人的に何かを確認するために、例外をキャッチするために嫌い:

他の可能な方法は、変数名がlocals()globals()であるかどうかを確認することです。それは見て、醜い感じ。

s = "84984x" 
try: 
    int(s) 
    do_something(s) 
except ValueError: 
    do_something_else(s) 

の代わりに優しくs.isdigit()を使用して:文字列が数字だけそのように含まれている場合は、チェックと同じです。 Eww。

+0

'int' /' isdigit'バージョンは重要な方法です。 'int'バージョンは負の数を許します。私は、負の数に対しては正しく動作し、無関係な先行ゼロを持つ数字は拒否します(Pythonのように)。 –

0

非常に古い質問ですが、本当に良い答えが必要です。短いプログラムでさえ、私はカスタム関数を使うといいでしょう!

例を示します。それはすべてのアプリケーションには完璧ではありませんが、私のために、無数のAPIからの応答を解析し、Djangoを使用しています。誰もが自分の要求に合わせて修正するのは簡単です。

from django.core.exceptions import ObjectDoesNotExist 
from functools import reduce 

class MultipleObjectsReturned(Exception): 
    pass 

def get_attr(obj, attr, default, asString=False, silent=True): 
    """ 
    Gets any attribute of obj. 
    Recursively get attributes by separating attribute names with the .-character.   
    Calls the last attribute if it's a function. 

    Usage: get_attr(obj, 'x.y.z', None) 
    """ 
    try: 
     attr = reduce(getattr, attr.split("."), obj) 
     if hasattr(attr, '__call__'): 
      attr = attr() 
     if attr is None: 
      return default 
     if isinstance(attr, list): 
      if len(attr) > 1: 
       logger.debug("Found multiple attributes: " + str(attr)) 
       raise MultipleObjectsReturned("Expected a single attribute") 
      else: 
       return str(attr[0]) if asString else attr[0] 
     else: 
      return str(attr) if asString else attr 
    except AttributeError: 
     if not silent: 
      raise 
     return default 
    except ObjectDoesNotExist: 
     if not silent: 
      raise 
     return default 
関連する問題