5

Python で問題が発生しましたが、明確な解決策が見つかりません...

いくつかのメソッドを呼び出すときに、メソッドの実行前と実行後にいくつかのコードを実行したい。context(他の多くのことの中で)変数を自動的に設定および消去するため。

これを実現するために、次のメタクラスを宣言しました。

class MyType(type):
    def __new__(cls, name, bases, attrs):
        #wraps the 'test' method to automate context management and other stuff
        attrs['test'] = cls.other_wrapper(attrs['test'])
        attrs['test'] = cls.context_wrapper(attrs['test'])
        return super(MyType, cls).__new__(cls, name, bases, attrs)

    @classmethod
    def context_wrapper(cls, operation):
        def _manage_context(self, *args, **kwargs):
            #Sets the context to 'blabla' before the execution
            self.context = 'blabla'
            returned = operation(self, *args, **kwargs)
            #Cleans the context after execution
            self.context = None
            return returned
        return _manage_context

    @classmethod
    def other_wrapper(cls, operation):
        def _wrapped(self, *args, **kwargs):
            #DO something with self and *args and **kwargs
            return operation(self, *args, **kwargs)
        return _wrapped

これは魅力のように機能します:

class Parent(object):

    __metaclass__ = MyType

    def test(self):
        #Here the context is set:
        print self.context #prints blabla

しかし、 をサブクラス化するとすぐにParent、親メソッドを次のように呼び出すと、問題が発生しますsuper

class Child(Parent):
    def test(self):
        #Here the context is set too
        print self.context #prints blabla
        super(Child, self).test()
        #But now the context is unset, because Parent.test is also wrapped by _manage_context
        #so this prints 'None', which is not what was expected
        print self.context

新しい値に設定する前にコンテキストを保存することを考えましたが、それは問題を部分的にしか解決しません...

確かに、(ちょっと待ってください、これは説明が難しいです)、親メソッドが呼び出され、ラッパーが実行されますが、それらは を受け取り*args**kwargsアドレス指定されますParent.testwhileselfChildインスタンスであるためself*argsand **kwargs(たとえば、自動検証の目的で)、例:

@classmethod
def validation_wrapper(cls, operation):
    def _wrapped(self, *args, **kwargs):
        #Validate the value of a kwarg
        #But if this is executed because we called super(Child, self).test(...
        #`self.some_minimum` will be `Child.some_minimum`, which is irrelevant
        #considering that we called `Parent.test`
        if not kwarg['some_arg'] > self.some_minimum:
            raise ValueError('Validation failed')
        return operation(self, *args, **kwargs)
    return _wrapped

したがって、基本的に、この問題を解決するには、次の 2 つの解決策があります。

  1. メソッドが呼び出されたときにラッパーが実行されるのを防ぎますsuper(Child, self)

  2. self常に「正しい」タイプのを持っている

両方の解決策は私には不可能に思えます...誰かがこれを解決する方法について考えを持っていますか? 提案 ?

4

3 に答える 3

1

では、コンテキストが既に に設定されているかどうかを確認することはできません_manage_contextか? このような:

def _manage_context(self, *args, **kwargs):
    #Sets the context to 'blabla' before the execution
    if self.context is None:
        self.context = 'blabla'
        returned = operation(self, *args, **kwargs)
        #Cleans the context after execution
        self.context = None
        return returned
    else:
        return operation(self, *args, **kwargs)

また、これはおそらく try-catch ブロックでラップして、例外が発生した場合にコンテキストを確実にリセットする必要があります。

于 2011-03-07T12:43:54.900 に答える
0

わかりました、最初に、あなたの「解決策」は本当に醜いですが、あなたはそれを知っていると思います. :-) それでは、あなたの質問に答えてみましょう。

まず、暗黙の「質問」です。Python のコンテキスト マネージャーを使用しないのはなぜですか? それらは、実質的に無料で、はるかに優れた構文とエラー管理を提供します。contextlib モジュールを参照してください。非常に役立ちます。特にreentranty に関するセクションを参照してください。

次に、コンテキスト マネージャーをスタックしようとすると、通常は問題が発生することがわかります。再帰を適切にサポートするには、単一の値ではなく値のスタックが必要なので、これは驚くべきことではありません。[リダイレクト stdout などの再入可能 cm のソースを表示して、それがどのように処理されるかを確認できます。]したがって、context_wrapper は次のいずれかにする必要があります。

  • (クリーナー) のリストを保持し、self.contextコンテキストに入るときにそれに追加し、終了するときにそこからポップします。そうすれば、常にコンテキストを取得できます

  • (もっとあなたが望むものに似ています)単一self.contextの を保持しますが、グローバル値も保持しDEPTH、入ると1つ増加し、終了すると1つ減少し、DEPTHが0の場合、self.contextはNoneにリセットされます。

2 番目の質問については、よくわかりません。正しいタイプですself A が B のサブクラスであり、self が A のインスタンスである場合、それは B のインスタンスでもあります。self.some_minimum が「間違っている」場合、self を A のインスタンスと見なすか B のインスタンスと見なすかは、some_minimum が実際にはインスタンスではないことを意味します。 instance 属性は self ですが、class 属性は A または B です。A と B は(メタクラスの)異なるオブジェクトであるため、A と B で自由に異なる可能性があります。

于 2015-07-18T19:41:57.347 に答える
0

実際、メソッドが次のように呼び出されたときにラッパーが実行されないようにする方法を見つけましたsuper(Child, self)

class MyType(type):
    def __new__(cls, name, bases, attrs):
        #wraps the 'test' method to automate context management and other stuff
        new_class = super(MyType, cls).__new__(cls, name, bases, attrs)
        new_class.test = cls.other_wrapper(new_class.test, new_class)

    @classmethod
    def other_wrapper(cls, operation, new_class):
        def _wrapped(self, *args, **kwargs):
            #DO something with self and *args and **kwargs ...
            #ONLY if self is of type *new_class* !!!
            if type(self) == new_class:
                pass #do things
            return operation(self, *args, **kwargs)
        return _wrapped

そのように、呼び出すとき:

super(Child, self).a_wrapped_method

ラッピングコードはバイパスされます!!! それはかなりハックですが、動作します...

于 2011-03-07T15:15:30.473 に答える