web-dev-qa-db-ja.com

パラメータ付きデコレータ?

デコレータによる変数 'Insurance_mode'の転送に問題があります。私はそれを次のデコレータステートメントで行います。

 @execute_complete_reservation(True)
 def test_booking_gta_object(self):
     self.test_select_gta_object()

残念ながら、この文は機能しません。おそらく、この問題を解決するためのより良い方法があるでしょう。

def execute_complete_reservation(test_case,insurance_mode):
    def inner_function(self,*args,**kwargs):
        self.test_create_qsf_query()
        test_case(self,*args,**kwargs)
        self.test_select_room_option()
        if insurance_mode:
            self.test_accept_insurance_crosseling()
        else:
            self.test_decline_insurance_crosseling()
        self.test_configure_pax_details()
        self.test_configure_payer_details

    return inner_function
325
falek.marcin

def test_booking_gta_objectという意味ですか?とにかく、引数付きのデコレータの構文は少し異なります - 引数付きのデコレータは関数を取り、別の関数を返すべき関数を返すべきです。それで、それは本当に通常のデコレータを返すべきです。ちょっとわかりにくいでしょ?つまり、

def decorator_factory(argument):
    def decorator(function):
        def wrapper(*args, **kwargs):
            funny_stuff()
            something_with_argument(argument)
            result = function(*args, **kwargs)
            more_funny_stuff()
            return result
        return wrapper
    return decorator

ここで あなたは主題についてもっと読むことができます - それは呼び出し可能なオブジェクトを使ってこれを実装することも可能であり、それはまたそこで説明されます。

567
t.dubrownik

編集:デコレータのメンタルモデルの詳細については、 this をご覧ください。素晴らしいPycon Talk。 30分の価値があります。

引数付きデコレータについて考える一つの方法は

@decorator
def foo(*args, **kwargs):
    pass

に変換します

foo = decorator(foo)

そのため、デコレータに引数があれば、

@decorator_with_args(arg)
def foo(*args, **kwargs):
    pass

に変換します

foo = decorator_with_args(arg)(foo)

decorator_with_argsは、カスタム引数を受け取り、実際のデコレータを返す関数です(これは装飾された関数に適用されます)。

私は私のデコレータを簡単にするためにパーシャルを使った簡単なトリックを使います

from functools import partial

def _pseudo_decor(fun, argument):
    def ret_fun(*args, **kwargs):
        #do stuff here, for eg.
        print ("decorator arg is %s" % str(argument))
        return fun(*args, **kwargs)
    return ret_fun

real_decorator = partial(_pseudo_decor, argument=arg)

@real_decorator
def foo(*args, **kwargs):
    pass

更新:

上記のfooreal_decorator(foo)になります

関数を修飾することの1つの効果は、名前fooがデコレータ宣言で上書きされることです。 fooは、real_decoratorによって返されるものすべてによって「オーバーライド」されます。この場合は、新しい関数オブジェクトです。

fooのすべてのメタデータ、特にdocstringと関数名は上書きされます。

>>> print(foo)
<function _pseudo_decor.<locals>.ret_fun at 0x10666a2f0>

functools.wraps は、返される関数にdocstringとnameを「持ち上げる」ための便利な方法を提供します。

from functools import partial, wraps

def _pseudo_decor(fun, argument):
    # magic sauce to lift the name and doc of the function
    @wraps(fun)
    def ret_fun(*args, **kwargs):
        #do stuff here, for eg.
        print ("decorator arg is %s" % str(argument))
        return fun(*args, **kwargs)
    return ret_fun

real_decorator = partial(_pseudo_decor, argument=arg)

@real_decorator
def bar(*args, **kwargs):
    pass

>>> print(bar)
<function __main__.bar(*args, **kwargs)>
253
srj

私はとてもエレガントなアイデアを見せたいのですが。 t.dubrownikによって提案された解決策は常に同じであるパターンを示しています:あなたはデコレータがすることに関係なく3層のラッパーが必要です。

だから私はこれがメタデコレータ、つまりデコレータのデコレータの仕事だと思った。デコレータは関数なので、実際には引数を持つ通常のデコレータとして機能します。

def parametrized(dec):
    def layer(*args, **kwargs):
        def repl(f):
            return dec(f, *args, **kwargs)
        return repl
    return layer

これはパラメータを追加するために通常のデコレータに適用することができます。たとえば、関数の結果を2倍にするデコレータがあるとします。

def double(f):
    def aux(*xs, **kws):
        return 2 * f(*xs, **kws)
    return aux

@double
def function(a):
    return 10 + a

print function(3)    # Prints 26, namely 2 * (10 + 3)

@parametrizedを使えば、パラメータを持つ一般的な@multiplyデコレータを構築できます。

@parametrized
def multiply(f, n):
    def aux(*xs, **kws):
        return n * f(*xs, **kws)
    return aux

@multiply(2)
def function(a):
    return 10 + a

print function(3)    # Prints 26

@multiply(3)
def function_again(a):
    return 10 + a

print function(3)          # Keeps printing 26
print function_again(3)    # Prints 39, namely 3 * (10 + 3)

慣習的に、parametrizedデコレータの最初のパラメータは関数ですが、残りの引数はパラメータ化されたデコレータのパラメータに対応します。

興味深い使用例は、型保証された表明デコレータです。

import itertools as it

@parametrized
def types(f, *types):
    def rep(*args):
        for a, t, n in Zip(args, types, it.count()):
            if type(a) is not t:
                raise TypeError('Value %d has not type %s. %s instead' %
                    (n, t, type(a))
                )
        return f(*args)
    return rep

@types(str, int)  # arg1 is str, arg2 is int
def string_multiply(text, times):
    return text * times

print(string_multiply('hello', 3))    # Prints hellohellohello
print(string_multiply(3, 3))          # Fails miserably with TypeError

最後の注意:ここではラッパー関数にfunctools.wrapsを使っていませんが、いつも使うことをお勧めします。

71
Dacav

これは t.dubrownikの答え を少し修正したものです。どうして?

  1. 一般的なテンプレートとして、元の関数からの戻り値を返すべきです。
  2. これにより関数の名前が変わり、他のデコレータやコードに影響を与える可能性があります。

したがって、 @functools.wraps() を使用してください。

from functools import wraps

def decorator(argument):
    def real_decorator(function):
        @wraps(function)
        def wrapper(*args, **kwargs):
            funny_stuff()
            something_with_argument(argument)
            retval = function(*args, **kwargs)
            more_funny_stuff()
            return retval
        return wrapper
    return real_decorator
57
Ross R

あなたの問題はあなたのデコレータに引数を渡していると思います。これは少しトリッキーで簡単なことではありません。

これを行う方法の例を示します。

class MyDec(object):
    def __init__(self,flag):
        self.flag = flag
    def __call__(self, original_func):
        decorator_self = self
        def wrappee( *args, **kwargs):
            print 'in decorator before wrapee with flag ',decorator_self.flag
            original_func(*args,**kwargs)
            print 'in decorator after wrapee with flag ',decorator_self.flag
        return wrappee

@MyDec('foo de fa fa')
def bar(a,b,c):
    print 'in bar',a,b,c

bar('x','y','z')

プリント:

in decorator before wrapee with flag  foo de fa fa
in bar x y z
in decorator after wrapee with flag  foo de fa fa

詳しくはBruce Eckelの記事を見てください。

35
Ross Rogers
def decorator(argument):
    def real_decorator(function):
        def wrapper(*args):
            for arg in args:
                assert type(arg)==int,f'{arg} is not an interger'
            result = function(*args)
            result = result*argument
            return result
        return wrapper
    return real_decorator

デコレータの使い方

@decorator(2)
def adder(*args):
    sum=0
    for i in args:
        sum+=i
    return sum

そうして

adder(2,3)

作り出す

10

しかし

adder('hi',3)

作り出す

---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
<ipython-input-143-242a8feb1cc4> in <module>
----> 1 adder('hi',3)

<ipython-input-140-d3420c248ebd> in wrapper(*args)
      3         def wrapper(*args):
      4             for arg in args:
----> 5                 assert type(arg)==int,f'{arg} is not an interger'
      6             result = function(*args)
      7             result = result*argument

AssertionError: hi is not an interger
3
Gajendra D Ambi

私の例では、新しいデコレータ関数を作成するために、これを1行のラムダで解決することにしました。

def finished_message(function, message="Finished!"):

    def wrapper(*args, **kwargs):
        output = function(*args,**kwargs)
        print(message)
        return output

    return wrapper

@finished_message
def func():
    pass

my_finished_message = lambda f: finished_message(f, "All Done!")

@my_finished_message
def my_func():
    pass

if __== '__main__':
    func()
    my_func()

実行すると、次のように出力されます。

Finished!
All Done!

おそらく他のソリューションほど拡張性はありませんが、私のために働きました。

2
ZacBook

カスタマイズされたデコレータ関数を生成するには、この「デコレータ関数」を定義します。

def decoratorize(FUN, **kw):
    def foo(*args, **kws):
        return FUN(*args, **kws, **kw)
    return foo

このように使ってください。

    @decoratorize(FUN, arg1 = , arg2 = , ...)
    def bar(...):
        ...
0
chen.wq

これは、パラメーターを指定しない場合に()を必要としない関数デコレーターのテンプレートです。

import functools


def decorator(x_or_func=None, *decorator_args, **decorator_kws):
    def _decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kws):
            if 'x_or_func' not in locals() \
                    or callable(x_or_func) \
                    or x_or_func is None:
                x = ...  # <-- default `x` value
            else:
                x = x_or_func
            return func(*args, **kws)

        return wrapper

    return _decorator(x_or_func) if callable(x_or_func) else _decorator

この例を以下に示します。

def multiplying(factor_or_func=None):
    def _decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            if 'factor_or_func' not in locals() \
                    or callable(factor_or_func) \
                    or factor_or_func is None:
                factor = 1
            else:
                factor = factor_or_func
            return factor * func(*args, **kwargs)
        return wrapper
    return _decorator(factor_or_func) if callable(factor_or_func) else _decorator


@multiplying
def summing(x): return sum(x)

print(summing(range(10)))
# 45


@multiplying()
def summing(x): return sum(x)

print(summing(range(10)))
# 45


@multiplying(10)
def summing(x): return sum(x)

print(summing(range(10)))
# 450
0
norok2