46
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

後者を仮定することは、私には非常に自然に思えました。これには正当な理由があると確信しています。それは何ですか?

4

7 に答える 7

93

getattr はオブジェクトの辞書検索にアクセスするようなものなので、getattr 関数にピリオドを入れることはできません (ただし、サブクラス化やその他の Python 実装の詳細のために、それよりも少し複雑です)。

で「dir」関数を使用すると、オブジェクトの属性に対応する辞書キーが表示されます。この場合、文字列「bc」は辞書キーのセットに含まれていません。

これを行う唯一の方法getattrは、呼び出しをネストすることです。

getattr(getattr(a, "b"), "c")

幸いなことに、標準ライブラリにはより優れたソリューションがあります。

import operator
operator.attrgetter("b.c")(a)
于 2012-08-15T19:23:59.277 に答える
49

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'>
于 2013-01-14T18:37:20.133 に答える
8

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、期待どおりに機能しません。

于 2012-08-15T19:50:47.353 に答える
6

その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")
于 2012-08-15T19:26:09.117 に答える
6

あなたが望むものを達成するための最も簡単な方法は、を使用することだと思います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'
于 2016-08-23T09:46:52.473 に答える
1

ドット演算子を分割し、ドット演算子ごとに 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') を介して実行できます。これにより、文字列内のドット操作の数を気にせずに操作が一般化されます。

于 2016-05-16T11:05:50.563 に答える
0

何を返す必要がありgetattr('a.b', {'a': None}, 'default-value'}ますか? レイズするAttributeErrorか、単に返す必要があり'default-value'ますか? そのため、複雑なキーを導入するgetattrと、使用がわかりにくくなります。

したがって、getattr(..)関数getをオブジェクト属性のディクショナリのメソッドと見なす方が自然です。

于 2012-08-15T19:34:06.920 に答える