web-dev-qa-db-ja.com

__getattr__と__getattribute__の違い

__getattr__または__getattribute__をいつ使用するかを理解しようとしています。 ドキュメント は、__getattribute__が新しいスタイルのクラスに適用されることに言及しています。新しいスタイルのクラスとは何ですか?

347
Yarin

__getattr____getattribute__の主な違いは、__getattr__は、属性が通常の方法で見つからなかった場合にのみ呼び出されることです。これは、欠落している属性のフォールバックを実装するのに適していますが、おそらく2つのうちの1つです。

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

新しいスタイルのクラスはobjectから派生し、古いスタイルのクラスは、明示的な基本クラスを持たないPython 2.xのクラスです。ただし、__getattr____getattribute__を選択する場合、古いスタイルのクラスと新しいスタイルのクラスの区別は重要ではありません。

ほぼ確実に__getattr__が必要です。

408
Ned Batchelder

__getattr____getattribute__の両方のマジックメソッドの簡単な例を見てみましょう。

__getattr__

Pythonは、まだ定義されていない属性を要求するたびに__getattr__メソッドを呼び出します。次の例では、私のクラスCountには__getattr__メソッドがありません。メインでobj1.myminobj1.mymaxの両方の属性にアクセスしようとすると、すべてが正常に機能します。しかし、obj1.mycurrent属性にアクセスしようとすると-PythonがAttributeError: 'Count' object has no attribute 'mycurrent'をくれます

class Count():
    def __init__(self,mymin,mymax):
        self.mymin=mymin
        self.mymax=mymax

obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.mycurrent)  --> AttributeError: 'Count' object has no attribute 'mycurrent'

現在、私のクラスCountには__getattr__メソッドがあります。 obj1.mycurrent属性にアクセスしようとすると、pythonが__getattr__メソッドで実装したものを返します。私の例では、存在しない属性を呼び出そうとするたびに、Pythonはその属性を作成し、整数値0に設定します。

class Count:
    def __init__(self,mymin,mymax):
        self.mymin=mymin
        self.mymax=mymax    

    def __getattr__(self, item):
        self.__dict__[item]=0
        return 0

obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.mycurrent1)

__getattribute__

次に、__getattribute__メソッドを見てみましょう。クラスに__getattribute__メソッドがある場合、pythonは、存在するかどうかに関係なく、すべての属性に対してこのメ​​ソッドを呼び出します。では、なぜ__getattribute__メソッドが必要なのでしょうか?正当な理由の1つは、次の例に示すように、属性へのアクセスを禁止し、より安全にすることができることです。

誰かが部分文字列'cur'で始まる私の属性にアクセスしようとするたびに、AttributeError例外が発生します。それ以外の場合は、その属性を返します。

class Count:

    def __init__(self,mymin,mymax):
        self.mymin=mymin
        self.mymax=mymax
        self.current=None

    def __getattribute__(self, item):
        if item.startswith('cur'):
            raise AttributeError
        return object.__getattribute__(self,item) 
        # or you can use ---return super().__getattribute__(item)

obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.current)

重要:__getattribute__メソッドでの無限再帰を回避するために、その実装は常に同じ名前で基本クラスメソッドを呼び出して、必要な属性にアクセスする必要があります。たとえば、self.__dict__[item]ではなく、object.__getattribute__(self, name)またはsuper().__getattribute__(item)

重要

クラスにgetattrgetattributeの両方のマジックメソッドが含まれている場合、__getattribute__が最初に呼び出されます。ただし、__getattribute__AttributeError例外を発生させた場合、例外は無視され、__getattr__メソッドが呼び出されます。次の例を参照してください。

class Count(object):

    def __init__(self,mymin,mymax):
        self.mymin=mymin
        self.mymax=mymax
        self.current=None

    def __getattr__(self, item):
            self.__dict__[item]=0
            return 0

    def __getattribute__(self, item):
        if item.startswith('cur'):
            raise AttributeError
        return object.__getattribute__(self,item)
        # or you can use ---return super().__getattribute__(item)
        # note this class subclass object

obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.current)
96
N Randhawa

新しいスタイルのクラスは、object、または別の新しいスタイルのクラスから継承します。

class SomeObject(object):
    pass

class SubObject(SomeObject):
    pass

古いスタイルのクラスにはありません:

class SomeObject:
    pass

これはPython 2にのみ適用されます-Python 3では、上記のすべてが新しいスタイルのクラスを作成します。

9。Classes(Pythonチュートリアル)、 NewClassVsClassicClass およびPythonの古いスタイルクラスと新しいスタイルクラスの違いは何ですか?.

15
Sam Dolan

これは、 Ned Batchelderの説明 に基づく単なる例です。

__getattr__の例:

class Foo(object):
    def __getattr__(self, attr):
        print "looking up", attr
        value = 42
        self.__dict__[attr] = value
        return value

f = Foo()
print f.x 
#output >>> looking up x 42

f.x = 3
print f.x 
#output >>> 3

print ('__getattr__ sets a default value if undefeined OR __getattr__ to define how to handle attributes that are not found')

そして、__getattribute__で同じ例を使用すると、>>> RuntimeError: maximum recursion depth exceeded while calling a Python objectが得られます

14

新しいスタイルのクラスは、「オブジェクト」を(直接または間接的に)サブクラス化するクラスです。これらには、__new__に加えて__init__クラスメソッドがあり、より合理的な低レベルの動作があります。

通常、__getattr__をオーバーライドする必要があります(どちらかをオーバーライドする場合)。そうしないと、メソッド内で "self.foo"構文をサポートするのが難しくなります。

追加情報: http://www.devx.com/opensource/Article/31482/0/page/4

6
Mr Fooz