0

すべてのオブジェクトが基本オブジェクトから派生したコードを持っていますが、これを直接インスタンス化する予定はありません。基本オブジェクトの__init__()メソッドで、いくつかの魔法を実行しようとしています。初期化されるオブジェクトのすべてのメソッドを装飾またはラップしようとしています。しかし、結果のメソッドを呼び出すと、困惑する結果が得られます。問題を特定するコード例を次に示します。

class ParentObject(object):
    def __init__(self):
        self._adjust_methods(self.__class__)

    def _adjust_methods(self, cls):
        for attr, val in cls.__dict__.iteritems():
            if callable(val) and not attr.startswith("_"):
                setattr(cls, attr, self._smile_warmly(val))
        bases = cls.__bases__
        for base in bases:
            if base.__name__ != 'object':
                self._adjust_methods(base)

    def _smile_warmly(self, the_method):
        def _wrapped(cls, *args, **kwargs):
            print "\n-smile_warmly - " +cls.__name__
            the_method(self, *args, **kwargs)
        cmethod_wrapped = classmethod(_wrapped)
        return cmethod_wrapped

class SonObject(ParentObject):
    def hello_son(self):
        print "hello son"

    def get_sister(self):
        sis = DaughterObject()
        print type(sis)
        return sis

class DaughterObject(ParentObject):
    def hello_daughter(self):
        print "hello daughter"

    def get_brother(self):
        bro = SonObject()
        print type(bro)
        return bro

if __name__ == '__main__':
    son = SonObject()
    son.hello_son()

    daughter = DaughterObject()
    daughter.hello_daughter()

    sis = son.get_sister()
    print type(sis)
    sis.hello_daughter()

    bro = sis.get_brother()
    print type(bro)
    bro.hello_son()

ただし、プログラムはクラッシュします。この行sis = son.get_sister()により、sisオブジェクトのタイプが NoneType になります。出力は次のとおりです。

-smile_warmly - SonObject
hello son

-smile_warmly - DaughterObject
hello daughter

-smile_warmly - SonObject
<class '__main__.DaughterObject'>
<type 'NoneType'>
Traceback (most recent call last):
  File "metaclass_decoration_test.py", line 48, in <module>
    sis.hello_daughter()
AttributeError: 'NoneType' object has no attribute 'hello_daughter'

なぜこうなった?

4

4 に答える 4

4

変更してみてください:

    def _wrapped(cls, *args, **kwargs):
        print "\n-smile_warmly - " +cls.__name__
        the_method(self, *args, **kwargs)

    def _wrapped(cls, *args, **kwargs):
        print "\n-smile_warmly - " +cls.__name__
        return the_method(self, *args, **kwargs)

_wrappedメソッドはラップされているメソッドを呼び出していますが、そのメソッドの戻り値を返していません。

于 2012-12-31T05:29:26.753 に答える
2

まあ、私はこのコードで起こっている狂気にさえ触れたくありませんが、あなたのエラーは特にあなたの「デコレータ」がラップされた関数から何も返さないためです:

def _smile_warmly(self, the_method):
    def _wrapped(cls, *args, **kwargs):
        print "\n-smile_warmly - " +cls.__name__
        return the_method(self, *args, **kwargs) # return here
    cmethod_wrapped = classmethod(_wrapped)
    return cmethod_wrapped
于 2012-12-31T05:35:41.533 に答える
1

問題は、を含むクラスのすべてのメソッドをラップしていることですget_sister。@Paul McGuire が示唆するように実行returnしてラッパーに追加することもできますが、これは、 を呼び出したときに「笑顔」のメッセージが出力されることを意味しますson.get_sister。これは、おそらく必要なものではありません。

_adjust_methods代わりに、どのメソッドをラップするかを正確に決定するロジックを内部に追加する必要があります。callableとをチェックするだけでなく、動作not startswith('_')でラップしたい、またはラップしたくないものに対して、いくつかの命名規則を設定できますsmile。ただし、これを行うほど、装飾したいメソッドを手動で装飾する場合と比較して、自動装飾の利点は少なくなります。明らかに使用したい構造 (すべてのクラスメソッド、すべてをラップするなど) を使用する理由を理解するのは少し難しいです。おそらく、ここで最終的な目標が何であるかを説明した場合、誰かがより簡単な設計を提案できるでしょう.

さらに、returnラッピングに または 追加のロジックを追加しても、他の質問で述べた問題が残ります。 でラッピングを行う__init__ため、クラスをインスタンス化するたびに発生するため、より多くのラッパーを追加し続けます。これが、クラス デコレータ、または必要に応じてメタクラスを使用することを提案した理由です。でクラス属性 (メソッドを含む) をいじる__init__のはお勧めできません。作成するインスタンスごとに、何度も何度もいじられるからです。

于 2012-12-31T05:38:32.917 に答える
1

@PaulMcGuire の返信に返されていないことがバグの原因です。

より高いレベルでは、メタクラスを介してより「一般的に」(これはほとんど一般的なアプローチではありません)継承を介して実行しようとしているようです。おそらく、このメタクラスの議論のようなものは、もう少し扱いやすい方向にあなたを指し示すでしょう.

于 2012-12-31T06:35:02.387 に答える