250

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

スタックオーバーフローの質問に対する答えvsの違い__getattr____getattribute__は次のとおりです。

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

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

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

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

なんで?

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

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

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

4

4 に答える 4

356

最初にいくつかの基本。

オブジェクトでは、それらの属性を処理する必要があります。通常、私たちはします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__常に呼び出されます。

于 2010-11-28T06:55:37.297 に答える
114

__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ため、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__'最も近い'スーパークラス(正式には、クラスのメソッド解決順序(MRO)の次のクラス)のメソッドを現在のオブジェクトにバインドし、selfそれを呼び出して、それが作業を実行できるようにします。

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

で無限再帰に遭遇する可能性があることにも注意してください__getattr__

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

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

于 2010-11-28T07:00:31.990 に答える
64

__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__

于 2010-11-28T07:35:08.293 に答える
10

私は他の人の素晴らしい説明を通り抜けました。しかし、私はこのブログPythonMagicMethodsと__getattr__から簡単な答えを見つけました。以下はすべてそこからです。

魔法の方法を使用して、__getattr__その存在しない属性ルックアップをインターセプトし、失敗しないように何かを行うことができます。

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属性が事実上失われたことを意味します。「到達不能」になっています。

于 2020-01-17T03:59:07.090 に答える