デコレータによる変数 '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
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
ここで あなたは主題についてもっと読むことができます - それは呼び出し可能なオブジェクトを使ってこれを実装することも可能であり、それはまたそこで説明されます。
編集:デコレータのメンタルモデルの詳細については、 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
更新:
上記のfoo
はreal_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)>
私はとてもエレガントなアイデアを見せたいのですが。 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
を使っていませんが、いつも使うことをお勧めします。
これは t.dubrownikの答え を少し修正したものです。どうして?
したがって、 @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
あなたの問題はあなたのデコレータに引数を渡していると思います。これは少しトリッキーで簡単なことではありません。
これを行う方法の例を示します。
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
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
私の例では、新しいデコレータ関数を作成するために、これを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!
おそらく他のソリューションほど拡張性はありませんが、私のために働きました。
カスタマイズされたデコレータ関数を生成するには、この「デコレータ関数」を定義します。
def decoratorize(FUN, **kw):
def foo(*args, **kws):
return FUN(*args, **kws, **kw)
return foo
このように使ってください。
@decoratorize(FUN, arg1 = , arg2 = , ...)
def bar(...):
...
これは、パラメーターを指定しない場合に()
を必要としない関数デコレーターのテンプレートです。
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