class A(): pass
a = A()
b = A()
a.b = b
b.c = 1
a.b # this is b
getattr(a, "b") # so is this
a.b.c # this is 1
getattr(a, "b.c") # this raises an AttributeError
後者を仮定することは、私には非常に自然に思えました。これには正当な理由があると確信しています。それは何ですか?
class A(): pass
a = A()
b = A()
a.b = b
b.c = 1
a.b # this is b
getattr(a, "b") # so is this
a.b.c # this is 1
getattr(a, "b.c") # this raises an AttributeError
後者を仮定することは、私には非常に自然に思えました。これには正当な理由があると確信しています。それは何ですか?
getattr はオブジェクトの辞書検索にアクセスするようなものなので、getattr 関数にピリオドを入れることはできません (ただし、サブクラス化やその他の Python 実装の詳細のために、それよりも少し複雑です)。
で「dir」関数を使用すると、オブジェクトの属性に対応する辞書キーが表示されます。この場合、文字列「bc」は辞書キーのセットに含まれていません。
これを行う唯一の方法getattr
は、呼び出しをネストすることです。
getattr(getattr(a, "b"), "c")
幸いなことに、標準ライブラリにはより優れたソリューションがあります。
import operator
operator.attrgetter("b.c")(a)
Python の組み込みreduce
関数は、探している機能を有効にします。ジョブを完了する簡単な小さなヘルパー関数を次に示します。
class NoDefaultProvided(object):
pass
def getattrd(obj, name, default=NoDefaultProvided):
"""
Same as getattr(), but allows dot notation lookup
Discussed in:
http://stackoverflow.com/questions/11975781
"""
try:
return reduce(getattr, name.split("."), obj)
except AttributeError, e:
if default != NoDefaultProvided:
return default
raise
テスト証明;
>>> getattrd(int, 'a')
AttributeError: type object 'int' has no attribute 'a'
>>> getattr(int, 'a')
AttributeError: type object 'int' has no attribute 'a'
>>> getattrd(int, 'a', None)
None
>>> getattr(int, 'a', None)
None
>>> getattrd(int, 'a', None)
None
>>> getattrd(int, '__class__.__name__')
type
>>> getattrd(int, '__class__')
<type 'type'>
a.b.c
混乱は、ストレートドット表記(例)が と同じパラメーターにアクセスするという事実から生じると思いますgetattr()
が、解析ロジックは異なります。どちらも基本的にオブジェクトの__dict__
属性にキーを設定しgetattr()
ますが、ドットでアクセス可能な属性に関するより厳しい要件に拘束されることはありません。例えば
setattr(foo, 'Big fat ugly string. But you can hash it.', 2)
その文字列は のハッシュキーになるだけなので、有効ですfoo.__dict__
が、
foo.Big fat ugly string. But you can hash it. = 2
と
foo.'Big fat ugly string. But you can hash it.' = 2
構文エラーです。これは、インタープリターにこれらのものを生のコードとして解析するように要求しているためです。これは機能しません。
これの反対側は、 whilefoo.b.c
が と同等でfoo.__dict__['b'].__dict__['c']
あり、getattr(foo, 'b.c')
が と同等であるということfoo.__dict__['b.c']
です。そのためgetattr
、期待どおりに機能しません。
そのgetattr
ようには機能しないからです。getattr
指定された名前(2番目の引数)を持つ指定されたオブジェクト(最初の引数)の属性を取得します。だからあなたのコード:
getattr(a, "b.c") # this raises an AttributeError
意味:「a」によって参照されるオブジェクトの「bc」属性にアクセスします。明らかに、オブジェクトには「」という属性がありませんb.c
。
「c」属性を取得するには、次の2つのgetattr
呼び出しを使用する必要があります。
getattr(getattr(a, "b"), "c")
理解を深めるために、それをアンラップしましょう。
b = getattr(a, "b")
c = getattr(b, "c")
あなたが望むものを達成するための最も簡単な方法は、を使用することだと思いますoperator.attrgetter
。
>>> import operator
>>> class B():
... c = 'foo'
...
>>> class A():
... b = B()
...
>>> a = A()
>>> operator.attrgetter('b.c')(a)
'foo'
属性が存在しない場合は、AttributeError
>>> operator.attrgetter('b.d')(a)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: B instance has no attribute 'd'
ドット演算子を分割し、ドット演算子ごとに getattr() を実行することで、関数内で関数を呼び出さずに複数の getattr を呼び出すことができます
def multi_getattr(self,obj, attr, default = None):
attributes = attr.split(".")
for i in attributes:
try:
obj = getattr(obj, i)
except AttributeError:
if default:
return default
else:
raise
return obj
abcd を呼び出したい場合は、a.multi_getattr('bcd') を介して実行できます。これにより、文字列内のドット操作の数を気にせずに操作が一般化されます。
何を返す必要がありgetattr('a.b', {'a': None}, 'default-value'}
ますか? レイズするAttributeError
か、単に返す必要があり'default-value'
ますか? そのため、複雑なキーを導入するgetattr
と、使用がわかりにくくなります。
したがって、getattr(..)
関数get
をオブジェクト属性のディクショナリのメソッドと見なす方が自然です。