2

コンテクスト:

関数を装飾して、それらの統計を追跡できるようにしたいと思います。この投稿を参照として使用して、独自の呼び出し可能なデコレータオブジェクトを作成しようとしました。

これが私が最終的に得たものです:

def Stats(fn):
    Class StatsObject(object):
        def __init__(self, fn):
            self.fn = fn
            self.stats = {}

        def __call__(self, obj, *args, **kwargs):
            self.stats['times_called'] = self.stats.get('times_called', 0) + 1
            return self.fn(obj, *args, **kwargs)

    function = StatsObject(fn)
    def wrapper(self, *args **kwargs):
        return function(self, *args, **kwargs)
    return wrapper

Class MockClass(object):
    @Stats
    def mock_fn(self, *args, **kwargs):
        # do things

問題:

これは実際にはmock_fn関数を正しく呼び出しますが、ラッパー関数の外部にstatsオブジェクトへの参照がありません。つまり、私はできません:

mc = MockClass()
mc.mock_fn()
mc.mock_fn.stats
# HasNoAttribute Exception

次に、スコーピングの問題であることを認識して、次のコードを変更してみました。

から:

    function = StatsObject(fn)
    def wrapper(self, *args **kwargs):
        return function(self, *args, **kwargs)
    return wrapper

に:

    function = StatsObject(fn)
    return function

しかしもちろん、私は自己参照を失いました(selfはStatsObjectインスタンスになり、objは最初の引数になり、MockClassオブジェクトの自己参照は失われます)。

だから私は最初の問題が起こっている理由を理解していますが、2番目の問題は起こっていません。MockClassの自己参照をStatsObject__call__関数に渡す方法はありますか?

4

1 に答える 1

2

関数自体は、実際にはPythonで属性を持つことができます。

def Stats(fn):
    class StatsObject(object):
        def __init__(self, fn):
            self.fn = fn
            self.stats = {}

        def __call__(self, obj, *args, **kwargs):
            self.stats['times_called'] = self.stats.get('times_called', 0) + 1
            return self.fn(obj, *args, **kwargs)

    function = StatsObject(fn)
    def wrapper(self, *args **kwargs):
        return function(self, *args, **kwargs)

    # KEY LINE BELOW: make the StatsObject available outside as "stats_fn"
    wrapper.stats_fn = function

    return wrapper

class MockClass(object):
    @Stats
    def mock_fn(self, *args, **kwargs):
        # do things

重要なのは、デコレータから返す関数の属性としてStatsObjectインスタンス(おそらく誤解を招くようにローカルで名前が付けられている)を割り当てることです。function

これを行うと、self.mock_fn.stats_fn.statsself.mock_fn()!属性は関数にあり、戻り値ではありません)は、のインスタンス内で機能し、MockClass外部MockClass.mock_fn.stats_fn.statsで使用できるようになります。MockClass統計は、 (デコレータがインスタンスごとに1回ではなく、1回呼び出されるため)のすべてのインスタンスにわたってグローバルになります。これは、必要な場合とそうでない場合があります。

于 2013-01-28T23:32:46.447 に答える