web-dev-qa-db-ja.com

Pythonメソッドまたは関数を装飾できるパラメータを持つクラスベースのデコレータ

私はPythonデコレータの多くの例を見てきました:

  • 関数スタイルデコレータ(関数のラッピング)
  • クラススタイルデコレータ(__init____get____call__の実装)
  • 引数を取らないデコレータ
  • 引数を取るデコレータ
  • 「メソッドフレンドリー」(つまり、クラスのメソッドを装飾できる)のデコレータ
  • 「関数フレンドリー」なデコレータ(プレーンな関数を装飾できる
  • メソッドと関数の両方を装飾できるデコレータ

しかし、上記のすべてを実行できる単一の例を見たことがなく、特定の質問( this one など)に対するさまざまな回答から合成することに問題があります。 this one または this one(これは私が今まで見た中で最高の答えの1つです) )、上記のすべてを組み合わせる方法。

私が欲しいのはclass-basedデコレータメソッドまたは関数、および 1つの追加パラメーター。つまり、次のように機能します。

class MyDecorator(object):
    def __init__(self, fn, argument):
        self.fn = fn
        self.arg = argument

    def __get__(self, ....):
        # voodoo magic for handling distinction between method and function here

    def __call__(self, *args, *kwargs):
        print "In my decorator before call, with arg %s" % self.arg
        self.fn(*args, **kwargs)
        print "In my decorator after call, with arg %s" % self.arg


class Foo(object):
    @MyDecorator("foo baby!")
    def bar(self):
        print "in bar!"


@MyDecorator("some other func!")
def some_other_function():
    print "in some other function!"

some_other_function()
Foo().bar()

そして私は見ることを期待します:

In my decorator before call, with arg some other func!
in some other function!
In my decorator after call, with arg some other func!
In my decorator before call, with arg foo baby!
in bar!
In my decorator after call, with arg foo baby!

編集:それが重要な場合は、Python 2.7。

41
Adam Parkin

記述子をいじる必要はありません。 __call__()メソッド内にラッパー関数を作成して返すだけで十分です。標準Python関数は、コンテキストに応じて、常にメソッドまたは関数として機能できます。

_class MyDecorator(object):
    def __init__(self, argument):
        self.arg = argument

    def __call__(self, fn):
        @functools.wraps(fn)
        def decorated(*args, **kwargs):
            print "In my decorator before call, with arg %s" % self.arg
            fn(*args, **kwargs)
            print "In my decorator after call, with arg %s" % self.arg
        return decorated
_

このデコレータを次のように使用するとどうなるかについて少し説明します。

_@MyDecorator("some other func!")
def some_other_function():
    print "in some other function!"
_

1行目はMyDecoratorのインスタンスを作成し、_"some other func!"_を引数として__init__()に渡します。このインスタンスを_my_decorator_と呼びましょう。次に、装飾されていない関数オブジェクト(_bare_func_と呼びましょう)が作成され、デコレータインスタンスに渡されるので、my_decorator(bare_func)が実行されます。これによりMyDecorator.__call__()が呼び出され、ラッパー関数が作成されて返されます。最後に、このラッパー関数は_some_other_function_という名前に割り当てられます。

38
Sven Marnach

レベルがありません。

コードを検討する

class Foo(object):
    @MyDecorator("foo baby!")
    def bar(self):
        print "in bar!"

このコードと同じです

class Foo(object):
    def bar(self):
        print "in bar!"
    bar = MyDecorator("foo baby!")(bar)

したがって、MyDecorator.__init__"foo baby!"で呼び出され、次にMyDecoratorオブジェクトは関数barで呼び出されます。

おそらくあなたはもっと何かを実装することを意味します

import functools

def MyDecorator(argument):
    class _MyDecorator(object):
        def __init__(self, fn):
            self.fn = fn

        def __get__(self, obj, type=None):
            return functools.partial(self, obj)

        def __call__(self, *args, **kwargs):
            print "In my decorator before call, with arg %s" % argument
            self.fn(*args, **kwargs)
            print "In my decorator after call, with arg %s" % argument

    return _MyDecorator
11
Mike Graham

デコレータのタイプのリストで、引数を取るかどうかに関係なく、デコレータを見逃しました。この例は、「関数スタイルデコレータ(関数のラッピング)」を除くすべてのタイプをカバーしていると思います

class MyDecorator(object):

    def __init__(self, argument):
        if hasattr('argument', '__call__'):
            self.fn = argument
            self.argument = 'default foo baby'
        else:
            self.argument = argument

    def __get__(self, obj, type=None):
        return functools.partial(self, obj)

    def __call__(self, *args, **kwargs):
        if not hasattr(self, 'fn'):
            self.fn = args[0]
            return self
        print "In my decorator before call, with arg %s" % self.argument
        self.fn(*args, **kwargs)
        print "In my decorator after call, with arg %s" % self.argument


class Foo(object):
    @MyDecorator("foo baby!")
    def bar(self):
        print "in bar!"

class Bar(object):
    @MyDecorator
    def bar(self):
        print "in bar!"

@MyDecorator
def add(a, b):
    print a + b