带参数的装饰器?

2024-04-20 06:37:53 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个问题的可变'保险模式'的转让装饰。我将通过下面的decorator语句来完成:

 @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

Tags: testselfexecuteobjectmodedefargsfunction
3条回答

带参数的decorators的语法有点不同-带参数的decorator应该返回一个函数,该函数将接受一个函数并返回另一个函数。所以它应该返回一个普通的装饰器。有点困惑,对吧?我的意思是:

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

Here您可以阅读有关此主题的更多信息-也可以使用可调用对象实现此功能,这里也将对此进行说明。

我想展示一个非常优雅的主意。dubrownik提出的解决方案显示了一个始终相同的模式:不管decorator做什么,都需要三层包装。

所以我认为这是一个元装饰器的工作,也就是装饰器的装饰器。由于decorator是一个函数,它实际上是一个带参数的常规decorator:

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

为了添加参数,这可以应用于常规的装饰器。例如,假设我们有一个decorator,它将函数的结果加倍:

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作为包装函数,但我建议您一直使用它。

编辑:要深入了解decorators的心理模型,请看一下this很棒的Pycon对话。很值得花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)

修饰函数的一个效果是在decorator声明时重写名称foofooreal_decorator返回的任何内容“重写”。在本例中,是一个新的函数对象。

所有foo的元数据都被重写,特别是docstring和函数名。

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

functools.wraps为我们提供了一个方便的方法来“提升”docstring和返回函数的名称。

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

相关问题 更多 >