web-dev-qa-db-ja.com

Pythonのデコレータクラス

次の原則をそのままにして、デコレータとして使用するクラスを構築したいと思います。

  1. このようなクラスデコレータを1つの関数の上に複数積み重ねることができるはずです。
  2. 結果の関数名ポインタは、デコレータがない同じ関数と区別がつかないはずです。おそらく、それがどのタイプ/クラスであるかを除いてください。
  3. デコレータによって実際に義務付けられていない限り、デコレータの注文は関係ありません。つまり、独立したデコレータは、任意の順序で適用できます。

これはDjangoプロジェクト用であり、現在取り組んでいる特定のケースでは、メソッドに2つのデコレータが必要であり、通常のpython関数:

@AccessCheck
@AutoTemplate
def view(request, item_id) {}

@AutoTemplateは関数を変更して、HttpResponseを返す代わりに、コンテキストで使用する辞書を返すようにします。 RequestContextが使用され、テンプレート名はメソッド名とモジュールから推測されます。

@AccessCheckは、item_idに基づいてユーザーに追加のチェックを追加します。

コンストラクターを正しく取得して適切な属性をコピーするだけだと思いますが、これらはどの属性ですか?

次のデコレータは、私が説明するようには機能しません。

class NullDecl (object):
    def __init__ (self, func):
        self.func = func
    def __call__ (self, * args):
        return self.func (*args)

次のコードで示されているように:

@NullDecl
@NullDecl
def decorated():
    pass

def pure():
    pass

# results in set(['func_closure', 'func_dict', '__get__', 'func_name',
# 'func_defaults', '__name__', 'func_code', 'func_doc', 'func_globals'])
print set(dir(pure)) - set(dir(decorated));

さらに、NullDeclコンストラクターに "print func .name"を追加してみてください。名前が欠落しているため、最初のデコレーターでは機能しますが、2番目のデコレーターでは機能しません。

洗練されたeduffyの答えは少し、それはかなりうまくいくようです:

class NullDecl (object):
    def __init__ (self, func):
        self.func = func
        for n in set(dir(func)) - set(dir(self)):
            setattr(self, n, getattr(func, n))

    def __call__ (self, * args):
        return self.func (*args)
    def __repr__(self):
        return self.func
38
Staale

何もしないデコレータクラスは次のようになります。

class NullDecl (object):
   def __init__ (self, func):
      self.func = func
      for name in set(dir(func)) - set(dir(self)):
        setattr(self, name, getattr(func, name))

   def __call__ (self, *args):
      return self.func (*args)

そして、あなたはそれを普通に適用することができます:

@NullDecl
def myFunc (x,y,z):
   return (x+y)/z
23
eduffy

デコレータモジュール は、署名を保持するデコレータを作成するのに役立ちます。

そして PythonDecoratorLibrary はデコレータの便利な例を提​​供するかもしれません。

10
f3lix

関数を元の関数と見分けがつかないようにラップするデコレータを作成するには、functools.wrapsを使用します。

例:


def mydecorator(func):
    @functools.wraps(func):
    def _mydecorator(*args, **kwargs):
        do_something()
        try:
            return func(*args, **kwargs)
        finally:
            clean_up()
    return _mydecorator

# ... and with parameters
def mydecorator(param1, param2):
    def _mydecorator(func):
        @functools.wraps(func)
        def __mydecorator(*args, **kwargs):
            do_something(param1, param2)
            try:
                return func(*args, **kwargs)
            finally:
                clean_up()
        return __mydecorator
    return _mydecorator

(私の個人的な好みは、クラスではなく関数を使用してデコレータを作成することです)

デコレータの順序は次のとおりです。


@d1
@d2
def func():
    pass

# is equivalent to
def func():
    pass

func = d1(d2(func))
7
codeape