__getattr__と__getattribute__の違いを理解する


206

私は違いを理解しようとしています__getattr____getattribute__、しかし、私はそれで失敗しています。

答えはスタックオーバーフローの質問に違い__getattr__Vsは__getattribute__言います:

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

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

次にそれは言う:

あなたはほぼ間違いなく欲しい__getattr__

どうして?

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

この質問をクリアするためのコード例を探しています。私は自分の能力を最大限に活用しましたが、私が見つけた答えは問題を完全に議論していません。

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


2
それが役立つ場合は、ドキュメントから「このメソッドでの無限再帰を回避するために、その実装は常に同じ名前で基本クラスメソッドを呼び出して、必要な属性にアクセスする必要があります(たとえば、object .__ getattribute __(self、name)。 」[ docs.python.org/2/reference/...
kmonsoor

回答:


306

最初にいくつかの基本事項。

オブジェクトでは、その属性を処理する必要があります。通常は行いますinstance.attribute。場合によっては、より詳細な制御が必要になることがあります(事前に属性の名前がわからない場合)。

たとえば、instance.attributeになりgetattr(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__ いつも呼ばれています。


「たとえば、instance.attributeはになりgetattr(instance, attribute_name)ます。」そうじゃないの__getattribute__(instance, attribute_name)
Md。Abu Nafee Ibna Zahid 2018

1
@ Md.AbuNafeeIbnaZahid getattrは組み込み関数です。getattr(foo, 'bar')と同等foo.barです。
wizzwizz4

__getattr__継承されていないため、定義されている場合にのみ呼び出されることに注意してくださいobject
joelb

94

__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

a属性へのアクセスを試みますf。これはを呼び出しますf.__getattribute__('a')__getattribute__その後、ロードを試みますself.__dict__。はの__dict__属性でself == fあり、そのためf.__getattribute__('__dict__')、属性へのアクセスを再試行するpython呼び出し'__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__「最も近い」スーパークラス(正式には、クラスのメソッド解決順序、またはMROの次のクラス)のメソッドを現在のオブジェクトにバインドし、selfそれを呼び出して、それを機能させます。

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

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

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

練習問題として残しておきます。


60

他の回答は__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__


6

私は他の人の素晴らしい説明をしました。しかし、私はこのブログPython Magic Methods and__getattr__から簡単な答えを見つけました。以下はすべてそこからです。

__getattr__magicメソッドを使用して、存在しない属性ルックアップをインターセプトし、失敗しないように何かを行うことができます。

class Dummy(object):

    def __getattr__(self, attr):
        return attr.upper()

d = Dummy()
d.does_not_exist # 'DOES_NOT_EXIST'
d.what_about_this_one  # 'WHAT_ABOUT_THIS_ONE'

ただし、属性が存在する場合は__getattr__呼び出されません。

class Dummy(object):

    def __getattr__(self, attr):
        return attr.upper()

d = Dummy()
d.value = "Python"
print(d.value)  # "Python"

__getattribute__はに似ていますが、すべての属性ルックアップをインターセプト__getattr__する重要な違いは、属性が存在するかどうかに関係なく重要です。__getattribute__

class Dummy(object):

    def __getattribute__(self, attr):
        return 'YOU SEE ME?'

d = Dummy()
d.value = "Python"
print(d.value)  # "YOU SEE ME?"

その例では、dオブジェクトにはすでに属性値があります。しかし、それにアクセスしようとすると、元の期待値(「Python」)を取得できません。__getattribute__返されたものは何でも取得しています。これは、value属性を実質的に失ったことを意味します。「到達不能」になりました。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.