带参数的装饰器?

馋奶兔 提交于 2020-02-27 00:44:08

我在装饰器传递变量'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

#1楼

编辑 :要深入了解装饰者的心理模型,请看一下这个很棒的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)

装饰函数的一种效果是名称foo在装饰器声明时被覆盖。 fooreal_decorator返回的值“覆盖”。 在这种情况下,一个新的功能对象。

foo的所有元数据都将被覆盖,尤其是docstring和函数名称。

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

functools.wraps为我们提供了一种方便的方法,可将文档字符串和名称“提升”为返回的函数。

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)>

#2楼

我想展示一个想法,恕我直言,非常优雅。 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作为包装函数,但是我建议一直使用它。


#3楼

这是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

#4楼

在我的实例中,我决定通过单行lambda解决此问题,以创建一个新的装饰器函数:

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()

执行后,将打印:

Finished!
All Done!

也许没有其他解决方案可扩展,但是为我工作。


#5楼

定义此“ decoratorize函数”以生成定制的装饰器函数:

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

使用这种方式:

    @decoratorize(FUN, arg1 = , arg2 = , ...)
    def bar(...):
        ...
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!