Decorators with parameters?
我有一个问题的变量'保险模式'的转移装饰。我可以通过下面的decorator语句来实现:
1 2 3 | @execute_complete_reservation(True) def test_booking_gta_object(self): self.test_select_gta_object() |
但不幸的是,这种说法不起作用。也许有更好的方法来解决这个问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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 |
号
你是说
1 2 3 4 5 6 7 8 9 10 | 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 |
在这里,您可以阅读更多关于这个主题的内容——也可以使用可调用对象来实现这一点,这里也解释了这一点。
编辑:为了深入了解装饰师的思维模式,请看这篇精彩的Pycon演讲。很值得30分钟。
有一种思考装饰师的方法是
1 2 3 | @decorator def foo(*args, **kwargs): pass |
。
转换为
1 | foo = decorator(foo) |
所以如果装饰师有争论,
1 2 3 | @decorator_with_args(arg) def foo(*args, **kwargs): pass |
。
转换为
1 | foo = decorator_with_args(arg)(foo) |
。
我用一个简单的技巧加上部分使我的装饰工容易些。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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 |
更新:
上面,
修饰函数的一个效果是名称
所有
1 2 | >>> print(foo) <function _pseudo_decor.<locals>.ret_fun at 0x10666a2f0> |
。
functools.wrapps为我们提供了一个方便的方法来"提升"返回函数的docstring和名称。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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提出的解决方案显示了一个始终相同的模式:无论装饰器做什么,您都需要三层包装器。
所以我认为这是一个元装饰师的工作,也就是说,一个装饰师的工作。由于decorator是一个函数,它实际上是一个带有参数的常规decorator:
1 2 3 4 5 6 | def parametrized(dec): def layer(*args, **kwargs): def repl(f): return dec(f, *args, **kwargs) return repl return layer |
。
这可以应用于常规装饰器以添加参数。例如,假设我们有一个decorator,它使函数的结果加倍:
1 2 3 4 5 6 7 8 9 10 | 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) |
号
使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | @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) |
号
通常,参数化装饰器的第一个参数是函数,而其余参数将对应于参数化装饰器的参数。
一个有趣的用法示例可以是类型安全断言修饰器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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 |
号
最后一点:这里我不使用
这是T.Dubrownik答案的一个稍微修改过的版本。为什么?
所以使用
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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 |
号
我猜你的问题是把论点传给你的装饰师。这有点棘手,也不简单。
下面是一个如何做到这一点的示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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') |
号
印刷品:
1 2 3 | 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 |
更多详情请参阅布鲁斯·埃克尔的文章。
1 2 3 4 5 6 7 8 9 10 | 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 |
号
装饰器的使用
1 2 3 4 5 6 | @decorator(2) def adder(*args): sum=0 for i in args: sum+=i return sum |
然后
1 | adder(2,3) |
。
生产
1 | 10 |
号
但是
1 | adder('hi',3) |
号
生产
1 2 3 4 5 6 7 8 9 10 11 12 13 | --------------------------------------------------------------------------- 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 |
在我的例子中,我决定通过一行lambda来解决这个问题,以创建一个新的decorator函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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 __name__ == '__main__': func() my_func() |
号
执行时,打印:
1 2 | Finished! All Done! |
号
也许不像其他解决方案那样可扩展,但对我有用。
定义此"decoratorize函数"以生成自定义的decorator函数:
1 2 3 4 | def decoratorize(FUN, **kw): def foo(*args, **kws): return FUN(*args, **kws, **kw) return foo |
号
使用方法如下:
1 2 3 | @decoratorize(FUN, arg1 = , arg2 = , ...) def bar(...): ... |
号