web-dev-qa-db-ja.com

'super'オブジェクトにはpython3の属性 '__getattr__'がありません

__getattr__をpython 3と継承でオーバーライドする方法は?

私が以下を使用するとき:

class MixinA:
    def __getattr__(self, item):
       # Process item and return value if known
       if item == 'a':
           return 'MixinA'

       # If it is unknown, pass it along to give 
       # a chance to another class to handle it
       return super().__getattr__(item)

class MixinB:
    def __getattr__(self, item):
       # Process item and return value if known
       if item == 'b':
           return 'MixinB'

       # If it is unknown, pass it along to give 
       # a chance to another class to handle it
       return super().__getattr__(item)

class Example(MixinA, MixinB):
    # main class
    pass

このエラーが発生します。

>>> e  = Example()
>>> e.a
'MixinA'
>>> e.b
'MixinB'
>>> e.c
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
...
AttributeError: 'super' object has no attribute '__getattr__'  

元のクラスとプロパティを参照している属性エラーを取得できませんでしたか?つまり、次のようになります。

AttributeError: 'Example' object has no attribute 'c'

PS:この投稿を見つけました 'super'オブジェクトが__getattr__を呼び出していません しかし、解決策があるかどうかはわかりません。

15
Michael

ミックスインスタイルのクラスで何かをしたい場合は、常にAttributeErrorを発生させるbase-attribute-getting-classを作成する必要があります。

class AttrGetter:
    def __getattr__(self, item):
        raise AttributeError(item)

class MixinA(AttrGetter):
    def __getattr__(self, item):
       if item == 'a':
           return 'MixinA'
       return super().__getattr__(item)

class MixinB(AttrGetter):
    def __getattr__(self, item):
       if item == 'b':
           return 'MixinB'
       return super().__getattr__(item)

class Example(MixinA, MixinB):
    pass

# mro stands for method resolution order.
# It defines the order in which classes are searched for attributes.
# We're looking for __getattr__ in this circumstance.
print(Example.mro())
# [<class '__main__.Example'>, <class '__main__.MixinA'>, <class '__main__.MixinB'>, 
#    <class '__main__.AttrGetter'>, <class 'object'>]
# As you can see, searches for __getattr__ only check AttrGetter after having checked 
# both mixins first.

e = Example()
print(e.a)
print(e.b)
print(e.c)

ただし、プロパティを使用すると、これをはるかに簡単で簡潔な方法で実行できます。

class MixinA:
    @property
    def a(self):
        return "MixinA"

class MixinB:
    @property
    def b(self):
        return "MixinB"

class Example(MixinA, MixinB):
    pass

e = Example()
print(e.a)
print(e.b)
print(e.c)
5
Dunes

Python属性取得メカニズムは、クラス__getattr__が「最後のリソース」として呼び出され、そのクラスのインスタンスの属性を取得しようとするように機能します。

コードは正しいです-「Example」のスーパークラス(この場合は「object」)が原因で失敗します-__getattr__属性がありません。クラスの階層が深くなく、オブジェクトから直接継承するクラスのカスタム属性取得を実行する場合(Py3では、コードの場合のようにベースがまったくないものとして表現できます)-「AttributeError」を発生させるだけです。カスタムルックアップが失敗した場合。

代わりに、カスタム検索をPythonの通常の属性取得メカニズムよりも優先させる(フォールバックとは呼ばない)場合は、__getattribute__ではなく__getattr__を実装する必要があります。

この場合、基本クラス(オブジェクト)には、通常の属性取得のために呼び出す必要のある__getattribute__メソッドがあります。問題は、すべてに対して呼び出す必要があることです。必要な-メソッド名、および設定した既知の属性を含みます。つまり:何かに沿って:

class Example:

    def __init__(self):
       self.attrs_to_override = ["attr1", "foo", "bar"]

    def docustomstuff(self, attr):
        ...

    def __getattribute__(self, attr):
       getter = super().__getattribute__
       if attr in getter("attrs_to_override"):
            getter("docustomstuff")(attr)
       return getter(attr)

つまり、__getattribute__ではなく__getattr__を実装する必要があると思われる場合は、非常に特殊な場合を除いて、おそらく間違ったアプローチを試みています。ここまで知らなかったのは、次のとおりです。__getattr__は、必要な名前の通常の属性がまだ存在しない場合は呼び出されなかったため、スーパークラスで呼び出す必要はありません(場合のスーパークラスはオブジェクトではなく、既知のカスタマイズがあります)

編集

または、次のスーパークラスに__getattr__が最もわかりやすい方法で含まれているかどうかを確認してください。

...
if hasattr(super(), "__getattr__"):
    return super().__getattr__(attr)
raise AttributeError
11
jsbueno