デフォルトの動作を壊さずにPythonで__getattr__をオーバーライドするにはどうすればよいですか?


185

__getattr__何か特別なことをするためにクラスのメソッドをオーバーライドしたいのですが、デフォルトの動作を壊したくありません。

これを行う正しい方法は何ですか?


20
「壊れる」と尋ねるのです。それは十分に明確です。「ファンシー」属性は組み込み属性に干渉してはならず、可能な限りそれらのように動作する必要があります。マイケルの答えは正しいと役に立ちます。
olooney

回答:


268

オーバーライド__getattr__は問題ありません- __getattr__最後の手段としてのみ呼び出されます。つまり、名前に一致するインスタンス内の属性がない場合です。たとえば、にアクセスするとfoo.bar、という属性がない__getattr__場合にのみfoo呼び出されbarます。属性が処理したくないものである場合は、次のようにレイズしAttributeErrorます。

class Foo(object):
    def __getattr__(self, name):
        if some_predicate(name):
            # ...
        else:
            # Default behaviour
            raise AttributeError

ただし、とは異なり__getattr____getattribute__最初に呼び出されます(新しいスタイルクラス、つまりオブジェクトから継承するクラスでのみ機能します)。この場合、次のようにデフォルトの動作を維持できます。

class Foo(object):
    def __getattribute__(self, name):
        if some_predicate(name):
            # ...
        else:
            # Default behaviour
            return object.__getattribute__(self, name)

詳細については、Pythonのドキュメントをご覧ください。


ええと、あなたの編集は私が私の答えで示していたものと同じもの、+ 1です。

12
__getattr__いいですね、Pythonはスーパーインを呼び出すのが好きではないようです-何かアイデアはありますか?(AttributeError: 'super' object has no attribute '__getattr__'
gatoatigrado 2013年

1
あなたのコードを見ることなく、それは伝えるのは難しいですが、あなたのスーパークラスのどれものように見えるが定義することGETATTRが
Colin vH 2015

これは、hasattrでも機能します。「これは、getattr(object、name)を呼び出して、例外が発生するかどうかを確認することで実装されます。」docs.python.org/2/library/functions.html#hasattr
ShaBANG

1
-1これデフォルトの動作を変更します。これAttributeErrorで、例外引数に属性のコンテキストがありません。
WIM

34
class A(object):
    def __init__(self):
        self.a = 42

    def __getattr__(self, attr):
        if attr in ["b", "c"]:
            return 42
        raise AttributeError("%r object has no attribute %r" %
                             (self.__class__.__name__, attr))

>>> a = A()
>>> a.a
42
>>> a.b
42
>>> a.missing
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 8, in __getattr__
AttributeError: 'A' object has no attribute 'missing'
>>> hasattr(a, "b")
True
>>> hasattr(a, "missing")
False

これをありがとう。ソースを掘り下げることなく、デフォルトのメッセージが正しいことを確認したかっただけです。
ShawnFumo 2013年

4
クラスがオーバーライドする場合のself.__class__.__name__代わりにそれを使用する必要があると思いself.__class__ます__repr__
Michael Scott Cuthbert

3
これは受け入れられた回答よりも優れていますが、その文言を変更したり、将来的に例外オブジェクトに追加のコンテキストを追加したりする場合に、そのコードを書き直さなくても、上流の変更を見逃さないようにするとよいでしょう。
WIM

13

Michaelの回答を拡張するために、を使用してデフォルトの動作を維持したい場合は__getattr__、次のようにすることができます。

class Foo(object):
    def __getattr__(self, name):
        if name == 'something':
            return 42

        # Default behaviour
        return self.__getattribute__(name)

これで、例外メッセージがよりわかりやすくなりました。

>>> foo.something
42
>>> foo.error
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in __getattr__
AttributeError: 'Foo' object has no attribute 'error'

2
@ fed.pavloよろしいですか?多分あなたは混合__getattr__しました__getattribute__か?
ホセ・ルイス・

私の悪い。自分とは違うメソッドの呼び出しを逃しました。;(
fed.pavlo

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