29

__getattribute__無限ループを避けるために、メソッドは慎重に記述する必要があります。例えば:

class A:
    def __init__(self):
        self.x = 100

    def __getattribute__(self, x):
        return self.x

>>> a = A()
>>> a.x    # infinite looop
RuntimeError: maximum recursion depth exceeded while calling a Python object


class B:
    def __init__(self):
        self.x = 100

    def __getattribute__(self, x):
        return self.__dict__[x]

>>> b = B()
>>> b.x    # infinite looop
RuntimeError: maximum recursion depth exceeded while calling a Python object

したがって、この方法でメソッドを記述する必要があります。

class C:
    def __init__(self):
        self.x = 100

    def __getattribute__(self, x):
        # 1. error
        # AttributeError: type object 'object' has no attribute '__getattr__'
        # return object.__getattr__(self, x)
        
        # 2. works
        return object.__getattribute__(self, x)
        
        # 3. works too
        # return super().__getattribute__(x)

私の質問は、なぜobject.__getattribute__メソッドが機能するのですか? メソッドはどこからobject取得し__getattribute__ますか? objectが何もない場合は__getattribute__、クラスで同じメソッドを呼び出していますCが、スーパークラスを介しています。なぜ、スーパークラスを介してメソッドを呼び出しても無限ループにならないのですか?

4

3 に答える 3

36

__getattribute__の実装は単なるフックであり、提供するとPythonがそれを呼び出し、そうでない場合はインタプリタが通常の魔法を直接実行するという印象を受けているようです。

それは正しくありません。Pythonがインスタンスの属性を検索する場合、__getattribute__はすべての属性アクセスのメインエントリでありobject、デフォルトの実装を提供します。したがって、実装は元の実装をオーバーライドし、実装が属性を返す代替手段を提供しない場合は失敗します。インスタンス( )へのすべての属性アクセスは。を介して再度チャネル化されるため、そのメソッドで属性アクセスを使用することはできませんselftype(self).__getattribute__(self, attr)

これを回避する最善の方法は、オーバーライドされたオリジナルを再度呼び出すことです。そこでsuper(C, self).__getattribute__(attr)登場します。クラス解決順序で次のクラスに属性アクセスを処理するように依頼しています。

または、unboundobject.__getattribute__()メソッドを直接呼び出すこともできます。このメソッドのC実装は、属性アクセスの最終停止です(直接アクセスできる__dict__ため、同じ制限に拘束されません)。

super()メソッド解決順序付けされた基本クラスで次に見つかるメソッドを検索するプロキシオブジェクトを返すことに注意してください。そのようなメソッドが存在しない場合、属性エラーで失敗します。元のメソッドを呼び出すことはありません。したがって、Foo.bar()検索super(Foo, self).barは基本クラスの実装または属性エラーのいずれかであり、それ自体ではありません Foo.bar

于 2012-11-24T05:03:58.630 に答える
10

これを行う場合:

    return object.__getattribute__(self, x)

特定の関数を呼び出しています-オブジェクトクラスで定義された関数であり、Aで定義された関数ではないため、再帰はありません。

これを行う場合:

    return self.x

Python に呼び出す関数を選択させ、A からの関数を呼び出すと、無限再帰が発生します。

于 2012-11-24T04:45:02.123 に答える