4

クラス内の特定のメソッドにデコレータを実装しようとしています。これにより、値がまだ計算されていない場合、メソッドは値を計算します。それ以外の場合は、インスタンスに格納されている事前計算された値を返しますdefaultdictdefaultdictクラスの外部で宣言されたデコレータの内部からインスタンスにアクセスする方法がわからないようです。これを実装する方法について何かアイデアはありますか?

インポートは次のとおりです(実際の例の場合)。

from collections import defaultdict
from math import sqrt

これが私のデコレータです:

class CalcOrPass:
    def __init__(self, func):
        self.f = func

    #if the value is already in the instance dict from SimpleData,        
    #don't recalculate the values, instead return the value from the dict
    def __call__(self, *args, **kwargs):

        # can't figure out how to access/pass dict_from_SimpleData to here :(
        res = dict_from_SimpleData[self.f.__name__]
        if not res:
            res = self.f(*args, **kwargs)
            dict_from_SimpleData[self.f__name__] = res
        return res

そして、これが装飾されたメソッドを持つSimpleDataクラスです。

class SimpleData:
    def __init__(self, data):
        self.data = data
        self.stats = defaultdict() #here's the dict I'm trying to access

    @CalcOrPass
    def mean(self):
        return sum(self.data)/float(len(self.data))

    @CalcOrPass
    def se(self):
        return [i - self.mean() for i in self.data]

    @CalcOrPass
    def variance(self):
        return sum(i**2 for i in self.se()) / float(len(self.data) - 1)

    @CalcOrPass
    def stdev(self):
        return sqrt(self.variance())

これまで、 SimpleDataでデコレータを宣言し、デコレータで複数の引数を渡そうとし(明らかにこれはできません)、紙飛行機をサソリの戦車に投げ込もうとしている間、回転椅子で回転してみました。どんな助けでもいただければ幸いです!

4

2 に答える 2

4

デコレータを定義する方法では、ターゲットオブジェクト情報が失われます。代わりに関数ラッパーを使用してください。

def CalcOrPass(func):
    @wraps(func)
    def result(self, *args, **kwargs):
        res = self.stats[func.__name__]
        if not res:
            res = func(self, *args, **kwargs)
            self.stats[func.__name__] = res
        return res
    return result

wrapsここからのものfunctoolsであり、厳密には必要ではありませんが、非常に便利です。


補足:defaultdictファクトリ関数の引数を取ります:

defaultdict(lambda: None)

ただし、とにかくキーの存在をテストしているので、単純なを優先する必要がありますdict

于 2012-04-15T21:04:12.560 に答える
2

関数が定義されているときは、バインドされていないため、やりたいことができません。実行時に一般的な方法でそれを実現する方法は次のとおりです。

class CalcOrPass(object):
    def __init__(self, func):
        self.f = func

    def __get__(self, obj, type=None): # Cheat.
        return self.__class__(self.f.__get__(obj, type))

    #if the value is already in the instance dict from SimpleData,
    #don't recalculate the values, instead return the value from the dict
    def __call__(self, *args, **kwargs):
        # I'll concede that this doesn't look very pretty.
        # TODO handle KeyError here
        res = self.f.__self__.stats[self.f.__name__]
        if not res:
            res = self.f(*args, **kwargs)
            self.f.__self__.stats[self.f__name__] = res
        return res

簡単な説明:

  • 私たちのデコレータは定義します(したがって、記述子__get__であると言われます)。属性アクセスのデフォルトの動作はオブジェクトのディクショナリから取得することですが、記述子メソッドが定義されている場合、Pythonは代わりにそれを呼び出します。
  • オブジェクトの場合は、object.__getattribute__次のようなアクセスを変換しb.xますtype(b).__dict__['x'].__get__(b, type(b))
  • このようにして、記述子のパラメーターからバインドされたクラスとそのタイプにアクセスできます。
  • 次に、新しいCalcOrPassオブジェクトを作成します。このオブジェクトは、古い非バインド関数の代わりに、バインドされたメソッドを装飾(ラップ)します。
  • 新しいスタイルクラスの定義に注意してください。私は試したことがないので、これが古いスタイルのクラスで機能するかどうかはわかりません。それらを使用しないでください。:)ただし、これは関数とメソッドの両方で機能します。
  • 「古い」装飾された機能に何が起こるかは、演習として残されています。
于 2012-04-15T21:23:54.680 に答える