属性の存在を確認するより良い方法はどれですか?
Jarret Hardie この答えを提供:
if hasattr(a, 'property'):
a.property
私もそれがこの方法でできることがわかります:
if 'property' in a.__dict__:
a.property
通常、1つのアプローチが他のアプローチよりも多く使用されていますか?
「最適な」方法はありません属性が存在するかどうかを確認するだけではないためです。それは常にいくつかの大きなプログラムの一部です。いくつかの正しい方法と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
出力:
'prop' in a .__ dict__ = False hasattr(a、 'prop')= True a.prop = 3
ほとんどの場合、__dict__
をいじりたくありません。これは、特別なことを行うための特別な属性であり、属性が存在するかどうかを確認することはごく当たり前のことです。
Pythonの一般的なイディオムは「許可よりも許しを求める方が簡単です」、または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
もちろん、hasattr
メソッドも機能します。この手法は、「先読みする」または略して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
をスローする可能性がありますが、これは非常にフェッチされたシナリオであり、とにかくスレッドについて非常に注意する必要があります。心配する価値がある。)
hasattr
の使用は、属性が存在するかどうかを知る必要がある限り、try/except
よりもはるかに簡単です。私にとって大きな問題は、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
は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
内部的に、getattr
およびhasattr
ビルトインはtry/except
テクニックを使用します(Cで記述されている場合を除く)。そのため、すべてが同じように動作し、正しいものを選ぶのは状況とスタイルの問題によるものです。
try/except
EAFPコードは常に一部のプログラマーを間違った方法でこすり、hasattr/getattr
LBYLコードは他のプログラマーを刺激します。それらは両方とも正しいものであり、どちらかを選択する真に説得力のある理由はしばしばありません。 (他のプログラマーは、属性が未定義であると通常だと考えることにうんざりしており、一部のプログラマーは、Pythonで未定義の属性を持つことさえ可能であることを恐れています。)
hasattr()
は道です*。
a.__dict__
は見苦しく、多くの場合機能しません。 hasattr()
は実際に属性を取得しようとし、AttributeError
を内部でキャッチするため、カスタム__getattr__()
メソッドを定義しても機能します。
属性の2回の要求を避けるために、getattr()
の3番目の引数を使用できます。
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: ..
が好きではありません。do_something()
関数内でAttributeError
を非表示にする場合があります。
*Python 3.1 hasattr()
すべての例外を抑制する前 (AttributeError
だけでなく)望ましくない場合は、getattr()
を使用する必要があります。
hasattr()
は、Pythonによる方法です。それを学び、愛してください。
他の可能な方法は、変数名がlocals()
またはglobals()
にあるかどうかをチェックすることです:
_if varName in locals() or in globals():
do_something()
else:
do_something_else()
_
私は個人的に何かをチェックするために例外をキャッチするのが嫌いです。見た目も感じもfeelいです。文字列に数字のみが含まれているかどうかを確認するのと同じです:
_s = "84984x"
try:
int(s)
do_something(s)
except ValueError:
do_something_else(s)
_
s.isdigit()
を優しく使用する代わりに。うわー.
非常に古い質問ですが、本当に良い答えが必要です。短いプログラムであっても、カスタム関数を使用するといいでしょう!
以下に例を示します。すべてのアプリケーションに最適というわけではありませんが、私にとっては、無数の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