web-dev-qa-db-ja.com

__getattr__と__getattribute__の違いを理解する

__getattr____getattribute__の違いを理解しようとしていますが、失敗しています。

answer Stack Overflowの質問__getattr____getattribute__の違い言う:

__getattribute__は、オブジェクトの実際の属性を見る前に呼び出されるため、正しく実装するのは難しい場合があります。あなたは非常に簡単に無限の再帰で終わることができます。

私はそれが何を意味するのか全く分かりません。

それからそれは言い続けます:

ほぼ間違いなく__getattr__が必要です。

どうして?

__getattribute__が失敗すると、__getattr__が呼び出されることを読みました。では、なぜ同じことを行う2つの異なる方法があるのでしょうか?コードで新しいスタイルクラスを実装する場合、何を使用すればよいですか?

この質問を解決するためのコード例を探しています。私は自分の能力を最大限に活用しましたが、見つけた答えは問題を徹底的に説明していません。

ドキュメントがある場合、それを読む準備ができています。

188
user225312

最初にいくつかの基本。

オブジェクトでは、その属性を処理する必要があります。通常、instance.attributeを実行します。事前に属性の名前がわからない場合は、さらに制御が必要な場合があります。

たとえば、instance.attributegetattr(instance, attribute_name)になります。このモデルを使用すると、文字列としてattribute_nameを指定することで属性を取得できます。

__getattr__の使用

また、__getattr__メソッドを使用して、明示的に管理しない属性を処理する方法をクラスに指示することもできます。

Pythonは、まだ定義されていない属性を要求するたびにこのメソッドを呼び出します。したがって、何をするかを定義できます。

古典的なユースケース:

class A(dict):
    def __getattr__(self, name):
       return self[name]
a = A()
# Now a.somekey will give a['somekey']

警告と__getattribute__の使用

すべての属性をキャッチする必要がある場合存在するかどうかに関係なく、代わりに__getattribute__を使用します。違いは、__getattr__は実際には存在しない属性に対してのみ呼び出されることです。属性を直接設定する場合、その属性を参照すると、__getattr__を呼び出さずに属性が取得されます。

__getattribute__は常に呼び出されます。

276
pyfunc

__getattribute__は、属性アクセスが発生するたびに呼び出されます。

class Foo(object):
    def __init__(self, a):
        self.a = 1

    def __getattribute__(self, attr):
        try:
            return self.__dict__[attr]
        except KeyError:
            return 'default'
f = Foo(1)
f.a

これにより、無限再帰が発生します。ここでの犯人は、行return self.__dict__[attr]です。すべての属性がself.__dict__に保存され、名前で使用できるようになります(真実に近い)。この線

f.a

af属性にアクセスしようとします。これはf.__getattribute__('a')を呼び出します。 __getattribute__は、self.__dict__をロードしようとします。 __dict__self == fの属性であるため、pythonはf.__getattribute__('__dict__')を呼び出して、再び属性'__dict__ 'にアクセスしようとします。これは無限の再帰です。

代わりに__getattr__が使用されていた場合

  1. fにはa属性があるため、実行されませんでした。
  2. 実行された場合(f.bを要求したとしましょう)、__dict__は既に存在し、__getattr__が呼び出されるのは、属性を検索する他のすべてのメソッドが失敗した場合

__getattribute__を使用して上記のクラスを記述する「正しい」方法は

class Foo(object):
    # Same __init__

    def __getattribute__(self, attr):
        return super(Foo, self).__getattribute__(attr)

super(Foo, self).__getattribute__(attr)は、「最近接」スーパークラスの__getattribute__メソッド(クラスのMethod Resolution OrderまたはMROの次のクラス)を現在のオブジェクトselfにバインドし、それを呼び出して作業を実行させます。 。

この問題はすべて、属性が見つからないまでPython 通常のことですを許可する__getattr__を使用することで回避できます。その時点で、Pythonは__getattr__メソッドに制御を渡し、何かを考え出させます。

また、__getattr__を使用して無限再帰を実行できることにも注意してください。

class Foo(object):
    def __getattr__(self, attr):
        return self.attr

これは演習として残しておきます。

85
aaronasterling

他の答えは__getattr____getattribute__の違いを説明するのに素晴らしい仕事をしたと思いますが、はっきりしないかもしれないことの1つは__getattribute__を使用する理由です。 __getattribute__の素晴らしい点は、クラスにアクセスするときにドットをオーバーロードできることです。これにより、低レベルでの属性へのアクセス方法をカスタマイズできます。たとえば、自己引数のみをとるすべてのメソッドがプロパティとして扱われるクラスを定義するとします。

# prop.py
import inspect

class PropClass(object):
    def __getattribute__(self, attr):
        val = super(PropClass, self).__getattribute__(attr)
        if callable(val):
            argcount = len(inspect.getargspec(val).args)
            # Account for self
            if argcount == 1:
                return val()
            else:
                return val
        else:
            return val

そして、インタラクティブなインタープリターから:

>>> import prop
>>> class A(prop.PropClass):
...     def f(self):
...             return 1
... 
>>> a = A()
>>> a.f
1

もちろんこれはばかげた例であり、おそらくこれを行うことはおそらくないでしょうが、__getattribute__をオーバーライドすることで得られる力を示しています。

58
Jason Baker