装饰者的参数?
我有一个由装饰器传输变量“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(argument): def real_decorator(function): def wrapper(*args, **kwargs): funny_stuff() something_with_argument(argument) function(*args, **kwargs) more_funny_stuff() return wrapper return real_decorator
在这里你可以阅读更多的主题 – 也可以使用可调用的对象来实现这一点,这也是解释的。
思考有装饰者的一种方法是
@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
即使这个问题已经得到了答复和接受,我想展示一个恕我直言,很优雅的想法。 t.dubrownik提出的解决方案显示了一个总是相同的模式:无论装饰器如何,都需要三层包装器。
所以我想这是一个元装饰器的工作,这是装饰器的装饰器。 作为一个装饰器是一个函数,它实际上是一个有参数的常规装饰器:
def parametrized(dec): def layer(*args, **kwargs): def repl(f): return dec(f, *args, **kwargs) return repl return layer
这可以应用到一个普通的装饰器,以添加参数。 所以举个例子,假设我们有一个将函数结果加倍的装饰器:
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)
通常,参数化装饰器的第一个参数是该函数,而其余参数将对应于参数化装饰器的参数。
一个有趣的用法示例可能是一个类型安全的断言装饰器:
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
的包装函数,但我会建议使用它所有的时间。
我认为你的问题是将参数传递给你的装饰器。 这有点棘手,并不是直截了当的。
这是一个如何做到这一点的例子:
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 xyz in decorator after wrapee with flag foo de fa fa
有关更多详细信息,请参阅Bruce Eckel的文章。
这里是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