我有一个由装饰器转移变量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

当前回答

这是curry函数的一个很好的用例。

curry函数本质上是延迟函数的调用,直到提供了所有输入。

这可以用于各种事情,如包装器或函数式编程。在本例中,让我们创建一个接受输入的包装器。

我将使用一个简单的包pamda,其中包含一个用于python的curry函数。这可以用作其他函数的包装器。

安装 Pamda:

pip install pamda

创建一个简单的带有两个输入的装饰函数:

@pamda.curry()
def my_decorator(input, func):
    print ("Executing Decorator")
    print(f"input:{input}")
    return func

使用提供给目标函数的第一个输入应用你的装饰器:

@my_decorator('Hi!')
def foo(input):
    print('Executing Foo!')
    print(f"input:{input}")

执行你的包装函数:

x=foo('Bye!')

把所有东西放在一起:

from pamda import pamda

@pamda.curry()
def my_decorator(input, func):
    print ("Executing Decorator")
    print(f"input:{input}")
    return func

@my_decorator('Hi!')
def foo(input):
    print('Executing Foo!')
    print(f"input:{input}")

x=foo('Bye!')

将:

Executing Decorator
input:Hi!
Executing Foo!
input:Bye!

其他回答

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

but

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

我想展示一个想法,在我看来很优雅。t.dubrownik提出的解决方案显示了一个始终相同的模式:无论装饰器做什么,您都需要三层包装器。

所以我认为这是一个元装饰师的工作,也就是说,装饰师的装饰师。由于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)

使用@ parameterized,我们可以构建一个带参数的通用@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。包装器函数,但我建议始终使用它。

这是curry函数的一个很好的用例。

curry函数本质上是延迟函数的调用,直到提供了所有输入。

这可以用于各种事情,如包装器或函数式编程。在本例中,让我们创建一个接受输入的包装器。

我将使用一个简单的包pamda,其中包含一个用于python的curry函数。这可以用作其他函数的包装器。

安装 Pamda:

pip install pamda

创建一个简单的带有两个输入的装饰函数:

@pamda.curry()
def my_decorator(input, func):
    print ("Executing Decorator")
    print(f"input:{input}")
    return func

使用提供给目标函数的第一个输入应用你的装饰器:

@my_decorator('Hi!')
def foo(input):
    print('Executing Foo!')
    print(f"input:{input}")

执行你的包装函数:

x=foo('Bye!')

把所有东西放在一起:

from pamda import pamda

@pamda.curry()
def my_decorator(input, func):
    print ("Executing Decorator")
    print(f"input:{input}")
    return func

@my_decorator('Hi!')
def foo(input):
    print('Executing Foo!')
    print(f"input:{input}")

x=foo('Bye!')

将:

Executing Decorator
input:Hi!
Executing Foo!
input:Bye!

编写一个带参数和不带参数的装饰器是一个挑战,因为Python在这两种情况下期望完全不同的行为!许多答案都试图解决这个问题,下面是@norok2对答案的改进。具体来说,这种变化消除了locals()的使用。

下面是@norok2给出的相同示例:

import functools

def multiplying(f_py=None, factor=1):
    assert callable(f_py) or f_py is None
    def _decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            return factor * func(*args, **kwargs)
        return wrapper
    return _decorator(f_py) if callable(f_py) 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(factor=10)
def summing(x): return sum(x)

print(summing(range(10)))
# 450

玩一下这段代码。

问题是用户必须提供键、值对的参数,而不是位置参数,并且第一个参数是保留的。

众所周知,下面两段代码几乎是等价的:

@dec
def foo():
    pass    foo = dec(foo)

############################################
foo = dec(foo)

一个常见的错误是认为@只是隐藏了最左边的参数。

@dec(1, 2, 3)
def foo():
    pass    
###########################################
foo = dec(foo, 1, 2, 3)

如果@是这样工作的,那么编写装饰器会容易得多。不幸的是,事情不是这样的。


考虑decorator Waitwhich haults 程序执行几秒钟。 如果你没有通过等待时间 缺省值为1秒。 用例如下所示。

##################################################
@Wait
def print_something(something):
    print(something)

##################################################
@Wait(3)
def print_something_else(something_else):
    print(something_else)

##################################################
@Wait(delay=3)
def print_something_else(something_else):
    print(something_else)

当Wait有一个参数,比如@Wait(3),那么调用Wait(3) 在发生任何其他事情之前执行。

也就是说,下面两段代码是等价的

@Wait(3)
def print_something_else(something_else):
    print(something_else)

###############################################
return_value = Wait(3)
@return_value
def print_something_else(something_else):
    print(something_else)

这是一个问题。

if `Wait` has no arguments:
    `Wait` is the decorator.
else: # `Wait` receives arguments
    `Wait` is not the decorator itself.
    Instead, `Wait` ***returns*** the decorator

解决方案如下:

让我们从创建以下类开始,DelayedDecorator:

class DelayedDecorator:
    def __init__(i, cls, *args, **kwargs):
        print("Delayed Decorator __init__", cls, args, kwargs)
        i._cls = cls
        i._args = args
        i._kwargs = kwargs
    def __call__(i, func):
        print("Delayed Decorator __call__", func)
        if not (callable(func)):
            import io
            with io.StringIO() as ss:
                print(
                    "If only one input, input must be callable",
                    "Instead, received:",
                    repr(func),
                    sep="\n",
                    file=ss
                )
                msg = ss.getvalue()
            raise TypeError(msg)
        return i._cls(func, *i._args, **i._kwargs)

现在我们可以这样写:

 dec = DelayedDecorator(Wait, delay=4)
 @dec
 def delayed_print(something):
    print(something)

注意:

dec does not not accept multiple arguments. dec only accepts the function to be wrapped. import inspect class PolyArgDecoratorMeta(type): def call(Wait, *args, **kwargs): try: arg_count = len(args) if (arg_count == 1): if callable(args[0]): SuperClass = inspect.getmro(PolyArgDecoratorMeta)[1] r = SuperClass.call(Wait, args[0]) else: r = DelayedDecorator(Wait, *args, **kwargs) else: r = DelayedDecorator(Wait, *args, **kwargs) finally: pass return r import time class Wait(metaclass=PolyArgDecoratorMeta): def init(i, func, delay = 2): i._func = func i._delay = delay def __call__(i, *args, **kwargs): time.sleep(i._delay) r = i._func(*args, **kwargs) return r

下面两段代码是等价的:

@Wait
def print_something(something):
     print (something)

##################################################

def print_something(something):
    print(something)
print_something = Wait(print_something)

我们可以非常缓慢地将“something”打印到控制台,如下所示:

print_something("something")

#################################################
@Wait(delay=1)
def print_something_else(something_else):
    print(something_else)

##################################################
def print_something_else(something_else):
    print(something_else)

dd = DelayedDecorator(Wait, delay=1)
print_something_else = dd(print_something_else)

##################################################

print_something_else("something")

最后指出

它可能看起来有很多代码,但你不必每次都写类DelayedDecorator和PolyArgDecoratorMeta。你必须亲自编写的代码如下所示,这是相当短的:

from PolyArgDecoratorMeta import PolyArgDecoratorMeta
import time
class Wait(metaclass=PolyArgDecoratorMeta):
 def __init__(i, func, delay = 2):
     i._func = func
     i._delay = delay

 def __call__(i, *args, **kwargs):
     time.sleep(i._delay)
     r = i._func(*args, **kwargs)
     return r