8

クラス A (他の誰かからインポートしているので変更できない) をクラス X にインスタンス化しています。

A のメソッドへの呼び出しをインターセプトまたはラップする方法はありますか? つまり、以下のコードで呼び出すことができます

x.a.p1()

出力を取得します

X.pre
A.p1
X.post

ティアがいっぱい!

class A:
    # in my real application, this is an imported class
    # that I cannot modify
    def p1(self): print 'A.p1'

class X:
    def __init__(self):
        self.a=A()
    def pre(self): print 'X.pre'
    def post(self): print 'X.post'

x=X()
x.a.p1()
4

6 に答える 6

6

私と同僚が思いついた解決策は次のとおりです。

from types import MethodType

class PrePostCaller:
    def __init__(self, other):
        self.other = other

    def pre(self): print 'pre'
    def post(self): print 'post'

    def __getattr__(self, name):
        if hasattr(self.other, name):
            func = getattr(self.other, name)
            return lambda *args, **kwargs: self._wrap(func, args, kwargs)
        raise AttributeError(name)

    def _wrap(self, func, args, kwargs):
        self.pre()
        if type(func) == MethodType:
            result = func( *args, **kwargs)
        else:
            result = func(self.other, *args, **kwargs)
        self.post()
        return result

#Examples of use
class Foo:
    def stuff(self):
        print 'stuff'

a = PrePostCaller(Foo())
a.stuff()

a = PrePostCaller([1,2,3])
print a.count()

与えます:

pre
stuff
post
pre
post
0

したがって、オブジェクトのインスタンスを作成するときは、それを PrePostCaller オブジェクトでラップします。その後、ラップされたオブジェクトのインスタンスであるかのようにオブジェクトを使用し続けます。このソリューションを使用すると、インスタンスごとにラッピングを行うことができます。

于 2008-11-03T10:18:21.910 に答える
1

A インスタンスを変更して、p1 関数をラッパー関数に置き換えるだけです。

def wrapped(pre, post, f):
    def wrapper(*args, **kwargs):
        pre()
        retval = f(*args, **kwargs)
        post()
        return retval
    return wrapper

class Y:
    def __init__(self):
        self.a=A()
        self.a.p1 = wrapped(self.pre, self.post, self.a.p1)

    def pre(self): print 'X.pre'
    def post(self): print 'X.post'
于 2008-11-03T09:06:35.177 に答える
1

最近Pythonのデコレータについて読んだばかりですが、まだ理解していませんが、問題の解決策になる可能性があるようです。http://www.artima.com/weblogs/viewpost.jsp?thread=240808で Bruce Eckel のデコレータの紹介を参照して ください。

彼はそこにそのトピックに関するいくつかの投稿を持っています.

編集: 3 日後、この記事に出くわしました。この記事では、デコレータを使用せずに同様のタスクを実行する方法と、それに関する問題を示し、デコレータを紹介し、完全なソリューションを開発します: http://wordaligned.org/articles/echo

于 2008-11-03T10:00:30.463 に答える
1

他の人が述べたように、ラッパー/デコレーターのソリューションはおそらく最も簡単なソリューションです。あなたが指摘したのと同じ理由で、ラップされたクラス自体を変更することはお勧めしません。

外部クラスが多数ある場合は、コード ジェネレーターを作成してラッパー クラスを生成できます。Python でこれを行っているので、おそらくジェネレーターをプログラムの一部として実装して、起動時にラッパーを生成することもできます。

于 2008-11-03T10:27:04.077 に答える
1

ホイッスルやベルのない解決策は、まさにそれを行うクラス A のラッパー クラスを作成することです。

于 2008-11-03T08:26:27.503 に答える
0

これは、comp.lang.python で Steven D'Aprano から受け取ったものです。

# Define two decorator factories.
def precall(pre):
    def decorator(f):
        def newf(*args, **kwargs):
            pre()
            return f(*args, **kwargs)
        return newf
    return decorator

def postcall(post):
    def decorator(f):
        def newf(*args, **kwargs):
            x = f(*args, **kwargs)
            post()
            return x
        return newf
    return decorator

必要に応じて、クラス A にモンキー パッチを適用できます。クラス A があらゆる場所で影響を受けるため、プロダクション コードでこれを行うのはおそらく良い考えではありません。[これは基本的にプロトコル コンバーターであり、処理される各クラスのインスタンスが 1 つだけあるため、私のアプリケーションでは問題ありません。]

class A:
    # in my real application, this is an imported class
    # that I cannot modify
    def p1(self): print 'A.p1'

class X:
    def __init__(self):
        self.a=A()
        A.p1 = precall(self.pre)(postcall(self.post)(A.p1))
    def pre(self): print 'X.pre'
    def post(self): print 'X.post'


x=X()
x.a.p1()

望ましい結果が得られます。

X.pre
A.p1
X.post
于 2008-11-03T10:07:25.693 に答える