__getattr__と__getattribute__の違い


418

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



5
@Trilarionまだその質問はここからの回答に言及しています...
JC Rocamonde 2018

回答:


497

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

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

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

あなたはほぼ確実に欲しいです__getattr__


6
両方を同じクラスに実装できますか?可能であれば、両方を実装することは何ですか?
Alcott、2011年

13
@Alcott:両方を実装できますが、なぜ実装するのかわかりません。 __getattribute__アクセスごと__getattr__に呼び出され、を__getattribute__発生させた時間に呼び出されAttributeErrorます。すべてを1つにまとめないのはなぜですか?
Ned Batchelder、2011年

10
@NedBatchelder、既存のメソッドの呼び出しを(条件付きで)オーバーライドする場合は、を使用します__getattribute__
ジェイスブラウニング

28
「このメソッドでの無限再帰を回避するために、その実装は常に同じ名前で基本クラスメソッドを呼び出して、必要な属性にアクセスする必要があります。たとえば、object .__ getattribute __(self、name)。」
kmonsoor 2014

1
@kmonsoor。これが両方を実装する良い理由です。適切な状況でobjec.__getattribute__呼び出しますmyclass.__getattr__
マッド物理学者

138

__getattr____getattribute__マジックメソッドの簡単な例をいくつか見てみましょう。

__getattr__

Pythonは__getattr__、まだ定義されていない属性をリクエストするたびにメソッドを呼び出し ます。次の例では、クラスCount__getattr__メソッドがありません。現在メインで両方にアクセスしようとするobj1.myminと、obj1.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'で始まる私の属性にアクセスしようとすると、Pythonは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__メソッドの無限再帰を回避するために、その実装は常に同じ名前で基本クラスメソッドを呼び出して、必要な属性にアクセスする必要があります。たとえば、次のobject.__getattribute__(self, name)super().__getattribute__(item)といませんself.__dict__[item]

重要

あなたのクラスが両方含まれている場合GETATTRのgetAttribute魔法の方法を、次に __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)

1
オーバーライドのユースケースが正確に何であるかはわかり__getattribute__ませんが、確かにそうではありません。あなたの例によれば、オブジェクトの属性がそこにない場合、あなたがしているすべて__getattribute__AttributeError例外を発生させているだけ__dict__です。ただし、これはのデフォルトの実装で__getattribute__あり、実際に__getattr__はフォールバックメカニズムとして必要なものなので、実際には必要ありません。
Rohit

@Rohitはcurrentのインスタンス上で定義されたCount(参照__init__)ので、単純に上げAttributeError、それがに延期-属性はかなり何が起こっていないが存在していない場合__getattr__のために、すべてを含め、名前「CUR」を起動currentするだけでなく、curiouscurly...
joelb

16

これは、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


9
実際、これはひどいです。実際の__getattr__()実装では、無効な属性名を発生させることにより、有効な属性名の有限セットのみを受け入れるため、微妙でデバッグが困難な問題AttributeErrorを回避できます。この例では、すべての属性名を無条件に有効として受け入れます。奇妙な(そして率直に言ってエラーが発生しやすい)の誤用です__getattr__()。この例のように属性の作成を「完全に制御」したい場合は、__getattribute__()代わりに必要です。
Cecil Curry

3
@CecilCurry:リンクしたすべての問題には、値ではなく暗黙的にNoneが返されますが、これはこの回答では行いません。すべての属性名を受け入れることの何が問題になっていますか?と同じdefaultdictです。
Nick Matteo

2
問題は、スーパークラスのルックアップの__getattr__に呼び出されることです。これは、の直接のサブクラスでは問題ありません。本当に気にする唯一のメソッドはとにかくインスタンスを無視する魔法のメソッドがあるためですが、より複雑な継承構造の場合は、親から何かを継承する機能を完全に削除します。object
Mad Physicist 2017

1
@Simon K Bhatta4ya、最後の印刷文はコメントです。正しい?これは長い行であり、読むのが面倒です(右側をたくさんスクロールする必要があります)。コードセクションの後に行を置くのはどうですか?または、コードセクションに配置する場合は、2つの異なる行に分割することをお勧めします。
Md。Abu Nafee Ibna Zahid 2018

14

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

class SomeObject(object):
    pass

class SubObject(SomeObject):
    pass

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

class SomeObject:
    pass

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

9.クラス(Pythonチュートリアル)、NewClassVsClassicClassおよびPythonの古いスタイルと新しいスタイルのクラスの違いは何ですか?を参照してください詳細については。


6

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

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

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


2

Beazley&Jones PCBを読む__getattr__ときに、OPの質問の「いつ」の部分に答えるのに役立つ明示的かつ実用的なユースケースに遭遇しました。本から:

「この__getattr__()メソッドは、属性ルックアップのキャッチオールのようなものです。存在しない属性にコードがアクセスしようとした場合に呼び出されるメソッドです。」これは上記の回答からわかりますが、PCBレシピ8.15では、この機能を使用して委任設計パターンを実装しています。オブジェクトAにオブジェクトBのメソッドを呼び出すためだけにオブジェクトAのすべてのオブジェクトBのメソッドを再定義するのではなく、オブジェクトAが委任する多くのメソッドを実装する属性オブジェクトBがある場合、__getattr__()メソッドを次のように定義します。

def __getattr__(self, name):
    return getattr(self._b, name)

ここで、_bはオブジェクトBであるオブジェクトAの属性の名前です。オブジェクトBで定義された__getattr__メソッドがオブジェクトAで呼び出されると、メソッドはルックアップチェーンの最後で呼び出されます。別のオブジェクトに委譲するためだけに定義されたメソッドのリストがないので、これによりコードもきれいになります。

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